refactor flows into canonical runtime architecture

This commit is contained in:
QLHazyCoder
2026-05-21 07:21:34 +08:00
parent e2485d2e64
commit a7b35ee11a
167 changed files with 9034 additions and 3032 deletions
+139 -121
View File
@@ -1,11 +1,17 @@
// background.js — Service Worker: orchestration, state, tab management, message routing
// background.js — Service Worker: orchestration, state, tab management, message routing
importScripts(
'shared/flow-registry.js',
'flows/openai/index.js',
'flows/openai/workflow.js',
'flows/kiro/index.js',
'flows/kiro/workflow.js',
'flows/index.js',
'core/flow-kernel/flow-registry.js',
'shared/contribution-registry.js',
'shared/settings-schema.js',
'shared/source-registry.js',
'shared/flow-capabilities.js',
'core/flow-kernel/settings-schema.js',
'imports/legacy/settings-importer.js',
'core/flow-kernel/source-registry.js',
'core/flow-kernel/flow-capabilities.js',
'shared/kiro-timeouts.js',
'managed-alias-utils.js',
'mail2925-utils.js',
@@ -25,15 +31,15 @@ importScripts(
'background/cpa-api.js',
'background/panel-bridge.js',
'background/registration-email-state.js',
'background/workflow-engine.js',
'background/runtime-state.js',
'background/kiro/state.js',
'background/kiro/credential-artifact.js',
'core/flow-kernel/workflow-engine.js',
'core/flow-kernel/runtime-state.js',
'flows/kiro/background/state.js',
'flows/kiro/background/credential-artifact.js',
'background/contribution/adapters/kiro-builder-id.js',
'background/kiro/register-runner.js',
'background/kiro/desktop-client.js',
'background/kiro/desktop-authorize-runner.js',
'background/kiro/publisher-kiro-rs.js',
'flows/kiro/background/register-runner.js',
'flows/kiro/background/desktop-client.js',
'flows/kiro/background/desktop-authorize-runner.js',
'flows/kiro/background/publisher-kiro-rs.js',
'background/generated-email-helpers.js',
'background/signup-flow-helpers.js',
'background/mail-rule-registry.js',
@@ -41,30 +47,30 @@ importScripts(
'background/message-router.js',
'background/verification-flow.js',
'background/auto-run-controller.js',
'background/tab-runtime.js',
'core/flow-kernel/tab-runtime.js',
'background/navigation-utils.js',
'background/logging-status.js',
'background/steps/registry.js',
'core/flow-kernel/logging-status.js',
'core/flow-kernel/step-registry.js',
'data/step-definitions.js',
'data/address-sources.js',
'background/steps/open-chatgpt.js',
'background/steps/submit-signup-email.js',
'background/steps/fill-password.js',
'background/steps/fetch-signup-code.js',
'background/steps/fill-profile.js',
'background/steps/wait-registration-success.js',
'background/steps/create-plus-checkout.js',
'background/steps/fill-plus-checkout.js',
'background/steps/gopay-manual-confirm.js',
'background/steps/paypal-approve.js',
'background/steps/gopay-approve.js',
'background/steps/plus-return-confirm.js',
'background/steps/sub2api-session-import.js',
'background/steps/cpa-session-import.js',
'background/steps/oauth-login.js',
'background/steps/fetch-login-code.js',
'background/steps/confirm-oauth.js',
'background/steps/platform-verify.js',
'flows/openai/background/steps/open-chatgpt.js',
'flows/openai/background/steps/submit-signup-email.js',
'flows/openai/background/steps/fill-password.js',
'flows/openai/background/steps/fetch-signup-code.js',
'flows/openai/background/steps/fill-profile.js',
'flows/openai/background/steps/wait-registration-success.js',
'flows/openai/background/steps/create-plus-checkout.js',
'flows/openai/background/steps/fill-plus-checkout.js',
'flows/openai/background/steps/gopay-manual-confirm.js',
'flows/openai/background/steps/paypal-approve.js',
'flows/openai/background/steps/gopay-approve.js',
'flows/openai/background/steps/plus-return-confirm.js',
'flows/openai/background/steps/sub2api-session-import.js',
'flows/openai/background/steps/cpa-session-import.js',
'flows/openai/background/steps/oauth-login.js',
'flows/openai/background/steps/fetch-login-code.js',
'flows/openai/background/steps/confirm-oauth.js',
'flows/openai/background/steps/platform-verify.js',
'data/names.js',
'hotmail-utils.js',
'microsoft-email.js',
@@ -498,15 +504,6 @@ function buildStatePatchWithRuntimeState(currentState = {}, updates = {}) {
if (runtimeStateHelpers?.buildSessionStatePatch) {
nextPatch = runtimeStateHelpers.buildSessionStatePatch(currentState, nextPatch);
}
if (kiroStateHelpers?.buildSessionStatePatch) {
const kiroPatch = kiroStateHelpers.buildSessionStatePatch(currentState, updates);
if (kiroPatch && Object.keys(kiroPatch).length > 0) {
nextPatch = {
...nextPatch,
...kiroPatch,
};
}
}
return nextPatch;
}
@@ -894,7 +891,7 @@ function buildResolvedStepDefinitionState(state = {}) {
signupMethod: requestedSignupMethod,
}, {
activeFlowId: requestedActiveFlowId,
panelMode: state?.panelMode,
targetId: state?.targetId,
signupMethod: requestedSignupMethod,
})
: null;
@@ -910,7 +907,6 @@ function buildResolvedStepDefinitionState(state = {}) {
...state,
activeFlowId: resolvedActiveFlowId,
flowId: resolvedActiveFlowId,
panelMode: stepDefinitionOptions.panelMode || capabilityState?.effectivePanelMode || state?.panelMode,
targetId: stepDefinitionOptions.targetId || capabilityState?.effectiveTargetId || state?.targetId,
plusModeEnabled: stepDefinitionOptions.plusModeEnabled === undefined
? plusModeEnabled
@@ -1178,9 +1174,8 @@ function setupDeclarativeNetRequestRules() {
// ============================================================
const PERSISTED_SETTING_DEFAULTS = {
panelMode: 'cpa',
targetId: 'cpa',
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
kiroTargetId: 'kiro-rs',
kiroRsUrl: String(self.MultiPageFlowRegistry?.DEFAULT_KIRO_RS_URL || '').trim(),
kiroRsKey: '',
vpsUrl: '',
@@ -1371,9 +1366,7 @@ const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState'];
const SETTINGS_SCHEMA_VIEW_KEYS = Object.freeze([
'activeFlowId',
'openaiIntegrationTargetId',
'panelMode',
'kiroTargetId',
'targetId',
'vpsUrl',
'vpsPassword',
'localCpaStep9Mode',
@@ -1417,7 +1410,6 @@ const DEFAULT_STATE = {
currentNodeId: '',
nodeStatuses: { ...DEFAULT_NODE_STATUSES },
runtimeState: runtimeStateHelpers?.buildDefaultRuntimeState?.() || null,
kiroRuntime: kiroStateHelpers?.buildDefaultRuntimeState?.() || null,
...CONTRIBUTION_RUNTIME_DEFAULTS,
accounts: [], // 已生成账号记录:{ email, password, createdAt }。
accountRunHistory: [], // 账号运行历史快照,实际持久化在 chrome.storage.local。
@@ -1819,7 +1811,7 @@ function resolveCurrentFlowCapabilities(state = {}, options = {}) {
}
return registry.resolveSidepanelCapabilities({
activeFlowId: options?.activeFlowId ?? state?.activeFlowId,
panelMode: options?.panelMode ?? state?.panelMode,
targetId: options?.targetId ?? state?.targetId,
signupMethod: options?.signupMethod ?? state?.signupMethod,
state,
});
@@ -1832,7 +1824,7 @@ function validateAutoRunStartState(state = {}, options = {}) {
}
return registry.validateAutoRunStart({
activeFlowId: options?.activeFlowId ?? state?.activeFlowId,
panelMode: options?.panelMode ?? state?.panelMode,
targetId: options?.targetId ?? state?.targetId,
signupMethod: options?.signupMethod ?? state?.signupMethod,
state,
});
@@ -1851,7 +1843,7 @@ function validateModeSwitchState(state = {}, options = {}) {
return registry.validateModeSwitch({
activeFlowId: options?.activeFlowId ?? state?.activeFlowId,
changedKeys: options?.changedKeys,
panelMode: options?.panelMode ?? state?.panelMode,
targetId: options?.targetId ?? state?.targetId,
signupMethod: options?.signupMethod ?? state?.signupMethod,
state,
});
@@ -1868,7 +1860,7 @@ function canUsePhoneSignup(state = {}) {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: state?.activeFlowId,
panelMode: state?.panelMode,
targetId: state?.targetId,
signupMethod: state?.signupMethod,
state,
})
@@ -1898,7 +1890,7 @@ function resolveSignupMethod(state = {}) {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: state?.activeFlowId,
panelMode: state?.panelMode,
targetId: state?.targetId,
signupMethod: method,
state,
})
@@ -3049,18 +3041,13 @@ function normalizeStepExecutionRangeByFlow(value = {}) {
function normalizePersistentSettingValue(key, value) {
switch (key) {
case 'panelMode':
return normalizePanelMode(value);
case 'targetId':
return String(value || '').trim().toLowerCase();
case 'activeFlowId':
if (typeof self.MultiPageFlowRegistry?.normalizeFlowId === 'function') {
return self.MultiPageFlowRegistry.normalizeFlowId(value, DEFAULT_ACTIVE_FLOW_ID);
}
return String(value || '').trim().toLowerCase() === 'kiro' ? 'kiro' : DEFAULT_ACTIVE_FLOW_ID;
case 'kiroTargetId':
if (typeof self.MultiPageFlowRegistry?.normalizeTargetId === 'function') {
return self.MultiPageFlowRegistry.normalizeTargetId('kiro', value, 'kiro-rs');
}
return String(value || '').trim().toLowerCase() === 'kiro-rs' ? 'kiro-rs' : 'kiro-rs';
case 'kiroRsUrl':
return String(value || '').trim();
case 'kiroRsKey':
@@ -3575,7 +3562,7 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
if (Object.prototype.hasOwnProperty.call(payload, 'phoneVerificationEnabled')
|| Object.prototype.hasOwnProperty.call(payload, 'plusModeEnabled')
|| Object.prototype.hasOwnProperty.call(payload, 'signupMethod')
|| Object.prototype.hasOwnProperty.call(payload, 'panelMode')
|| Object.prototype.hasOwnProperty.call(payload, 'targetId')
|| Object.prototype.hasOwnProperty.call(payload, 'activeFlowId')) {
payload.signupMethod = resolveSignupMethod(nextSignupConstraintState);
}
@@ -3689,6 +3676,13 @@ function mergeSettingsStatePatch(baseValue = {}, patchValue = {}) {
function buildSettingsStatePatchFromFlatUpdates(updates = {}) {
const patch = {};
const hasUpdate = (key) => Object.prototype.hasOwnProperty.call(updates, key);
const normalizePatchFlowId = (value = '', fallback = DEFAULT_ACTIVE_FLOW_ID) => {
if (typeof self.MultiPageFlowRegistry?.normalizeFlowId === 'function') {
return self.MultiPageFlowRegistry.normalizeFlowId(value, fallback);
}
const normalized = String(value || '').trim().toLowerCase();
return normalized || String(fallback || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
};
const assignIfUpdated = (key, path) => {
if (hasUpdate(key)) {
setSettingsStatePatchValue(patch, path, updates[key]);
@@ -3696,26 +3690,29 @@ function buildSettingsStatePatchFromFlatUpdates(updates = {}) {
};
assignIfUpdated('activeFlowId', ['activeFlowId']);
if (hasUpdate('openaiIntegrationTargetId') || hasUpdate('panelMode')) {
if (hasUpdate('selectedTargetId') || hasUpdate('targetId')) {
const flowId = normalizePatchFlowId(
updates.activeFlowId ?? updates.flowId,
DEFAULT_ACTIVE_FLOW_ID
);
setSettingsStatePatchValue(
patch,
['flows', 'openai', 'integrationTargetId'],
hasUpdate('openaiIntegrationTargetId') ? updates.openaiIntegrationTargetId : updates.panelMode
['flows', flowId, 'selectedTargetId'],
hasUpdate('selectedTargetId') ? updates.selectedTargetId : updates.targetId
);
}
assignIfUpdated('kiroTargetId', ['flows', 'kiro', 'targetId']);
assignIfUpdated('vpsUrl', ['flows', 'openai', 'integrationTargets', 'cpa', 'vpsUrl']);
assignIfUpdated('vpsPassword', ['flows', 'openai', 'integrationTargets', 'cpa', 'vpsPassword']);
assignIfUpdated('localCpaStep9Mode', ['flows', 'openai', 'integrationTargets', 'cpa', 'localCpaStep9Mode']);
assignIfUpdated('sub2apiUrl', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiUrl']);
assignIfUpdated('sub2apiEmail', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiEmail']);
assignIfUpdated('sub2apiPassword', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiPassword']);
assignIfUpdated('sub2apiGroupName', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiGroupName']);
assignIfUpdated('sub2apiGroupNames', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiGroupNames']);
assignIfUpdated('sub2apiAccountPriority', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiAccountPriority']);
assignIfUpdated('sub2apiDefaultProxyName', ['flows', 'openai', 'integrationTargets', 'sub2api', 'sub2apiDefaultProxyName']);
assignIfUpdated('codex2apiUrl', ['flows', 'openai', 'integrationTargets', 'codex2api', 'codex2apiUrl']);
assignIfUpdated('codex2apiAdminKey', ['flows', 'openai', 'integrationTargets', 'codex2api', 'codex2apiAdminKey']);
assignIfUpdated('vpsUrl', ['flows', 'openai', 'targets', 'cpa', 'vpsUrl']);
assignIfUpdated('vpsPassword', ['flows', 'openai', 'targets', 'cpa', 'vpsPassword']);
assignIfUpdated('localCpaStep9Mode', ['flows', 'openai', 'targets', 'cpa', 'localCpaStep9Mode']);
assignIfUpdated('sub2apiUrl', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiUrl']);
assignIfUpdated('sub2apiEmail', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiEmail']);
assignIfUpdated('sub2apiPassword', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiPassword']);
assignIfUpdated('sub2apiGroupName', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiGroupName']);
assignIfUpdated('sub2apiGroupNames', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiGroupNames']);
assignIfUpdated('sub2apiAccountPriority', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiAccountPriority']);
assignIfUpdated('sub2apiDefaultProxyName', ['flows', 'openai', 'targets', 'sub2api', 'sub2apiDefaultProxyName']);
assignIfUpdated('codex2apiUrl', ['flows', 'openai', 'targets', 'codex2api', 'codex2apiUrl']);
assignIfUpdated('codex2apiAdminKey', ['flows', 'openai', 'targets', 'codex2api', 'codex2apiAdminKey']);
assignIfUpdated('customPassword', ['services', 'account', 'customPassword']);
assignIfUpdated('signupMethod', ['flows', 'openai', 'signup', 'signupMethod']);
assignIfUpdated('phoneVerificationEnabled', ['flows', 'openai', 'signup', 'phoneVerificationEnabled']);
@@ -3850,6 +3847,18 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
const currentSettingsState = isPlainObjectValue(prevState?.settingsState)
? prevState.settingsState
: {};
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
? getSettingsSchemaApi()
: null;
const normalizedCurrentSettingsState = settingsSchemaApi?.normalizeSettingsState
? settingsSchemaApi.normalizeSettingsState({
...prevState,
activeFlowId,
settingsState: currentSettingsState,
}, {
activeFlowId,
})
: currentSettingsState;
const normalizedStepExecutionRangeByFlow = normalizeStepExecutionRangeByFlow(prevState?.stepExecutionRangeByFlow || {});
const nextSettingsStatePatch = {
activeFlowId,
@@ -3868,7 +3877,9 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
},
flows: {
openai: {
integrationTargetId: prevState?.openaiIntegrationTargetId || prevState?.panelMode,
selectedTargetId: settingsSchemaApi?.getSelectedTargetId
? settingsSchemaApi.getSelectedTargetId(normalizedCurrentSettingsState, 'openai')
: undefined,
autoRun: normalizedStepExecutionRangeByFlow.openai
? {
stepExecutionRange: normalizedStepExecutionRangeByFlow.openai,
@@ -3876,7 +3887,9 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
: undefined,
},
kiro: {
targetId: prevState?.kiroTargetId,
selectedTargetId: settingsSchemaApi?.getSelectedTargetId
? settingsSchemaApi.getSelectedTargetId(normalizedCurrentSettingsState, 'kiro')
: undefined,
autoRun: normalizedStepExecutionRangeByFlow.kiro
? {
stepExecutionRange: normalizedStepExecutionRangeByFlow.kiro,
@@ -3918,15 +3931,14 @@ function buildFreshAutoRunKeepState(prevState = {}) {
keepState.activeFlowId = activeFlowId;
keepState.flowId = activeFlowId;
if (Object.prototype.hasOwnProperty.call(sourceState, 'panelMode')) {
keepState.panelMode = normalizePanelMode(sourceState.panelMode);
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
? getSettingsSchemaApi()
: null;
if (settingsSchemaApi?.getSelectedTargetId) {
keepState.targetId = settingsSchemaApi.getSelectedTargetId(settingsState, activeFlowId);
}
if (typeof kiroStateHelpers?.buildFreshKeepState === 'function') {
Object.assign(keepState, kiroStateHelpers.buildFreshKeepState(sourceState));
} else if (Object.prototype.hasOwnProperty.call(sourceState, 'kiroTargetId')) {
keepState.kiroTargetId = self.MultiPageFlowRegistry?.normalizeTargetId
? self.MultiPageFlowRegistry.normalizeTargetId('kiro', sourceState.kiroTargetId, 'kiro-rs')
: String(sourceState.kiroTargetId || 'kiro-rs').trim().toLowerCase();
}
if (Object.prototype.hasOwnProperty.call(sourceState, 'settingsSchemaVersion')) {
keepState.settingsSchemaVersion = Number(sourceState.settingsSchemaVersion) || 0;
@@ -4172,7 +4184,15 @@ async function importSettingsBundle(configBundle) {
throw new Error('\u914d\u7f6e\u6587\u4ef6\u7f3a\u5c11 settings \u914d\u7f6e\u6bb5\u3002');
}
const importedSettings = buildPersistentSettingsPayload(configBundle.settings, {
const settingsImporter = self.MultiPageLegacySettingsImporter?.createSettingsImporter?.({
flowRegistry: self.MultiPageFlowRegistry,
settingsSchemaApi: typeof getSettingsSchemaApi === 'function' ? getSettingsSchemaApi() : null,
defaultFlowId: DEFAULT_ACTIVE_FLOW_ID,
}) || null;
const importedSettingsSource = typeof settingsImporter?.importSettings === 'function'
? settingsImporter.importSettings(configBundle.settings)
: configBundle.settings;
const importedSettings = buildPersistentSettingsPayload(importedSettingsSource, {
fillDefaults: true,
requireKnownKeys: true,
});
@@ -4190,7 +4210,7 @@ async function importSettingsBundle(configBundle) {
Object.prototype.hasOwnProperty.call(importedSettings, 'phoneVerificationEnabled')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'plusModeEnabled')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'signupMethod')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'panelMode')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'targetId')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'activeFlowId')
|| Object.prototype.hasOwnProperty.call(importedSettings, 'accountContributionEnabled')
) {
@@ -4436,7 +4456,7 @@ function buildAccountContributionState(enabled, persistedSettings = {}, currentS
...(routing ? {
contributionSource: routing.source,
contributionTargetGroupName: routing.targetGroupName,
panelMode: routing.source,
targetId: routing.source,
} : {}),
customPassword: '',
accountRunHistoryTextEnabled: false,
@@ -4449,7 +4469,7 @@ function buildAccountContributionState(enabled, persistedSettings = {}, currentS
accountContributionExpected: false,
contributionAdapterId: '',
flowContributionRuntime: {},
panelMode: persistedSettings.panelMode || DEFAULT_STATE.panelMode,
targetId: persistedSettings.targetId || DEFAULT_STATE.targetId,
customPassword: persistedSettings.customPassword || '',
accountRunHistoryTextEnabled: Boolean(persistedSettings.accountRunHistoryTextEnabled),
};
@@ -4472,7 +4492,7 @@ async function setAccountContributionMode(enabled, options = {}) {
}
broadcastDataUpdate({
...contributionBroadcast,
panelMode: nextState.panelMode,
targetId: nextState.targetId,
customPassword: nextState.customPassword,
accountRunHistoryTextEnabled: nextState.accountRunHistoryTextEnabled,
accountRunHistoryHelperBaseUrl: nextState.accountRunHistoryHelperBaseUrl,
@@ -8542,10 +8562,10 @@ function getPanelMode(state = {}) {
if (typeof navigationUtils !== 'undefined' && navigationUtils?.getPanelMode) {
return navigationUtils.getPanelMode(state);
}
if (state.panelMode === 'sub2api') {
if (state.targetId === 'sub2api') {
return 'sub2api';
}
if (state.panelMode === 'codex2api') {
if (state.targetId === 'codex2api') {
return 'codex2api';
}
return 'cpa';
@@ -8661,7 +8681,6 @@ function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
const reference = parseUrlSafely(referenceUrl);
switch (source) {
case 'openai-auth':
case 'signup-page':
return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname);
case 'duck-mail':
return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/');
@@ -8975,7 +8994,6 @@ function getSourceLabel(source) {
'openai-auth': '认证页',
'gmail-mail': 'Gmail 邮箱',
'sidepanel': '侧边栏',
'signup-page': '认证页',
'vps-panel': 'CPA 面板',
'sub2api-panel': 'SUB2API 后台',
'codex2api-panel': 'Codex2API 后台',
@@ -11544,7 +11562,7 @@ async function executeNodeAndWait(nodeId, delayAfter = 2000) {
}
if (normalizedNodeId === 'fill-profile') {
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
if (signupTabId) {
await addLog('自动运行:填写资料节点已收到完成信号,正在等待当前页面完成加载并稳定...', 'info');
await waitForTabStableComplete(signupTabId, {
@@ -12953,7 +12971,7 @@ async function runAutoSequenceFromNodeGraph(startNodeId, context = {}) {
continue;
}
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
if (signupTabId) {
await chrome.tabs.update(signupTabId, { active: true });
}
@@ -13236,9 +13254,9 @@ async function resumeAutoRun() {
// ============================================================
const SIGNUP_ENTRY_URL = 'https://chatgpt.com/';
const SIGNUP_PAGE_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/auth-page-recovery.js', 'content/phone-country-utils.js', 'content/phone-auth.js', 'content/signup-page.js'];
const KIRO_REGISTER_INJECT_FILES = ['shared/source-registry.js', 'shared/kiro-timeouts.js', 'content/utils.js', 'content/kiro/register-page.js'];
const KIRO_DESKTOP_AUTHORIZE_INJECT_FILES = ['shared/source-registry.js', 'shared/kiro-timeouts.js', 'content/utils.js', 'content/kiro/desktop-authorize-page.js'];
const OPENAI_AUTH_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/auth-page-recovery.js', 'flows/openai/content/phone-country-utils.js', 'flows/openai/content/phone-auth.js', 'flows/openai/content/openai-auth.js'];
const KIRO_REGISTER_INJECT_FILES = ['flows/openai/index.js', 'flows/kiro/index.js', 'flows/index.js', 'core/flow-kernel/flow-registry.js', 'core/flow-kernel/source-registry.js', 'shared/kiro-timeouts.js', 'content/utils.js', 'flows/kiro/content/register-page.js'];
const KIRO_DESKTOP_AUTHORIZE_INJECT_FILES = ['flows/openai/index.js', 'flows/kiro/index.js', 'flows/index.js', 'core/flow-kernel/flow-registry.js', 'core/flow-kernel/source-registry.js', 'shared/kiro-timeouts.js', 'content/utils.js', 'flows/kiro/content/desktop-authorize-page.js'];
const panelBridge = self.MultiPageBackgroundPanelBridge?.createPanelBridge({
chrome,
addLog,
@@ -13287,7 +13305,7 @@ const signupFlowHelpers = self.MultiPageSignupFlowHelpers?.createSignupFlowHelpe
setEmailState,
setState,
SIGNUP_ENTRY_URL,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
waitForTabStableComplete,
waitForTabUrlMatch,
});
@@ -13418,7 +13436,7 @@ const step2Executor = self.MultiPageBackgroundStep2?.createStep2Executor({
resolveSignupMethod,
resolveSignupEmailForFlow,
sendToContentScriptResilient,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
waitForTabStableComplete,
});
const step3Executor = self.MultiPageBackgroundStep3?.createStep3Executor({
@@ -13432,7 +13450,7 @@ const step3Executor = self.MultiPageBackgroundStep3?.createStep3Executor({
sendToContentScript,
setPasswordState,
setState,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
});
async function ensureIcloudMailSessionForVerification(options = {}) {
@@ -13917,7 +13935,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
},
finalizeStep3Completion: async () => {
const currentState = await getState();
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
return signupFlowHelpers.finalizeSignupPasswordSubmitInTab(
signupTabId,
currentState.password || currentState.customPassword || '',
@@ -14143,7 +14161,7 @@ async function executeStep2(state) {
}
// ============================================================
// Step 3: Fill Password (via signup-page.js)
// Step 3: Fill Password (via openai-auth.js)
// ============================================================
async function executeStep3(state) {
@@ -14151,7 +14169,7 @@ async function executeStep3(state) {
}
// ============================================================
// Step 4: Get Signup Verification Code (qq-mail.js polls, then fills in signup-page.js)
// Step 4: Get Signup Verification Code (qq-mail.js polls, then fills in openai-auth.js)
// ============================================================
function getMailConfig(state) {
@@ -14305,7 +14323,7 @@ async function executeStep4(state) {
}
// ============================================================
// Step 5: Fill Name & Birthday (via signup-page.js)
// Step 5: Fill Name & Birthday (via openai-auth.js)
// ============================================================
async function executeStep5(state) {
@@ -14659,7 +14677,7 @@ async function getLoginAuthStateFromContent(options = {}) {
const logStep = visibleStep > 0 ? visibleStep : null;
const { logMessage = '认证页正在切换,等待页面重新就绪后继续确认验证码页状态...' } = options;
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'GET_LOGIN_AUTH_STATE',
source: 'background',
@@ -14684,7 +14702,7 @@ async function getLoginAuthStateFromContent(options = {}) {
async function getStep5SubmitStateFromContent(options = {}) {
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'GET_STEP5_SUBMIT_STATE',
source: 'background',
@@ -14709,7 +14727,7 @@ async function getStep5SubmitStateFromContent(options = {}) {
async function recoverStep5SubmitRetryPageOnTab(options = {}) {
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'RECOVER_STEP5_SUBMIT_RETRY_PAGE',
source: 'background',
@@ -14849,7 +14867,7 @@ async function ensureStep8VerificationPageReady(options = {}) {
const recoverTimeoutMs = 15000;
if (typeof sendToContentScriptResilient === 'function') {
recoverResult = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
recoverMessage,
{
timeoutMs: recoverTimeoutMs,
@@ -14861,7 +14879,7 @@ async function ensureStep8VerificationPageReady(options = {}) {
}
);
} else if (typeof sendToContentScript === 'function') {
recoverResult = await sendToContentScript('signup-page', recoverMessage, {
recoverResult = await sendToContentScript('openai-auth', recoverMessage, {
responseTimeoutMs: recoverTimeoutMs,
});
}
@@ -15098,9 +15116,9 @@ async function shouldDeferStep9CallbackTimeout(details = {}) {
async function ensureStep8SignupPageReady(tabId, options = {}) {
const visibleStep = Math.floor(Number(options.visibleStep || options.logStep || options.step) || 0);
await ensureContentScriptReadyOnTab('signup-page', tabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', tabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: options.timeoutMs ?? 15000,
retryDelayMs: options.retryDelayMs ?? 600,
logMessage: options.logMessage || '',
@@ -15142,7 +15160,7 @@ async function readAuthTabSnapshot(tabId) {
async function getStep8PageState(tabId, responseTimeoutMs = 1500, visibleStep = 9) {
try {
const result = await sendTabMessageWithTimeout(tabId, 'signup-page', {
const result = await sendTabMessageWithTimeout(tabId, 'openai-auth', {
type: 'STEP8_GET_STATE',
source: 'background',
payload: { visibleStep },
@@ -15219,7 +15237,7 @@ async function prepareStep8DebuggerClick(tabId, options = {}) {
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本已失联,正在恢复后继续定位按钮...',
});
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'STEP8_FIND_AND_CLICK',
source: 'background',
payload: { visibleStep, nodeId: 'confirm-oauth' },
@@ -15249,7 +15267,7 @@ async function triggerStep8ContentStrategy(tabId, strategy, options = {}) {
logStepKey: 'confirm-oauth',
logMessage: '认证页内容脚本已失联,正在恢复后继续点击“继续”按钮...',
});
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'STEP8_TRIGGER_CONTINUE',
source: 'background',
payload: {
@@ -15287,7 +15305,7 @@ async function recoverAuthRetryPageOnTab(tabId, payload = {}, options = {}) {
logStepKey: 'confirm-oauth',
logMessage: options.readyLogMessage || '认证页内容脚本已失联,正在恢复后继续处理重试页...',
});
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'RECOVER_AUTH_RETRY_PAGE',
source: 'background',
payload: { nodeId: 'confirm-oauth', ...(payload || {}) },
+1 -2
View File
@@ -106,8 +106,7 @@
return {
activeFlowId: state.activeFlowId,
flowId: state.flowId || state.activeFlowId,
panelMode: state.panelMode,
kiroTargetId: state.kiroTargetId,
targetId: state.targetId,
vpsUrl: state.vpsUrl,
vpsPassword: state.vpsPassword,
customPassword: state.customPassword,
-1
View File
@@ -22,7 +22,6 @@
'openai-auth': '认证页',
'gmail-mail': 'Gmail 邮箱',
'sidepanel': '侧边栏',
'signup-page': '认证页',
'vps-panel': 'CPA 面板',
'sub2api-panel': 'SUB2API 后台',
'codex2api-panel': 'Codex2API 后台',
+17 -25
View File
@@ -91,7 +91,7 @@
}
return capabilityRegistry.validateAutoRunStart({
activeFlowId: options?.activeFlowId ?? validationState?.activeFlowId,
panelMode: options?.panelMode ?? validationState?.panelMode,
targetId: options?.targetId ?? validationState?.targetId,
signupMethod: options?.signupMethod ?? validationState?.signupMethod,
state: validationState,
});
@@ -113,7 +113,7 @@
return capabilityRegistry.validateModeSwitch({
activeFlowId: options?.activeFlowId ?? validationState?.activeFlowId,
changedKeys: options?.changedKeys,
panelMode: options?.panelMode ?? validationState?.panelMode,
targetId: options?.targetId ?? validationState?.targetId,
signupMethod: options?.signupMethod ?? validationState?.signupMethod,
state: validationState,
});
@@ -217,10 +217,6 @@
return String(targetId || fallbackSourceId).trim().toLowerCase() || fallbackSourceId;
}
function mapAutoRunTargetIdToPanelMode(targetId = '', fallback = 'cpa') {
return String(targetId || fallback || 'cpa').trim().toLowerCase() || 'cpa';
}
function buildAutoRunFlowStateUpdates(payload = {}) {
const hasActiveFlowId = Object.prototype.hasOwnProperty.call(payload, 'activeFlowId');
const hasTargetId = Object.prototype.hasOwnProperty.call(payload, 'targetId');
@@ -233,11 +229,11 @@
flowId: activeFlowId,
};
if (hasTargetId) {
if (activeFlowId === 'kiro') {
updates.kiroTargetId = normalizeMessageTargetId('kiro', payload.targetId, 'kiro-rs');
} else {
updates.panelMode = mapAutoRunTargetIdToPanelMode(payload.targetId, 'cpa');
}
updates.targetId = normalizeMessageTargetId(
activeFlowId,
payload.targetId,
activeFlowId === 'kiro' ? 'kiro-rs' : 'cpa'
);
}
return updates;
}
@@ -297,10 +293,10 @@
}
const signupTabId = typeof getTabId === 'function'
? await getTabId('signup-page')
? await getTabId('openai-auth')
: null;
const signupTabAlive = signupTabId && typeof isTabAlive === 'function'
? await isTabAlive('signup-page')
? await isTabAlive('openai-auth')
: Boolean(signupTabId);
if (!signupTabId || !signupTabAlive) {
@@ -1309,7 +1305,7 @@
const state = await getState();
const autoRunStartValidation = validateAutoRunStart(state, {
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
targetId: autoRunFlowStateUpdates.targetId ?? state?.targetId,
state,
});
if (autoRunStartValidation?.ok === false) {
@@ -1352,7 +1348,7 @@
const state = await getState();
const autoRunStartValidation = validateAutoRunStart(state, {
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
targetId: autoRunFlowStateUpdates.targetId ?? state?.targetId,
state,
});
if (autoRunStartValidation?.ok === false) {
@@ -1451,7 +1447,7 @@
Object.prototype.hasOwnProperty.call(updates, 'phoneVerificationEnabled')
|| Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
|| Object.prototype.hasOwnProperty.call(updates, 'signupMethod')
|| Object.prototype.hasOwnProperty.call(updates, 'panelMode')
|| Object.prototype.hasOwnProperty.call(updates, 'targetId')
|| Object.prototype.hasOwnProperty.call(updates, 'activeFlowId')
|| Object.prototype.hasOwnProperty.call(updates, 'accountContributionEnabled')
) {
@@ -1600,10 +1596,8 @@
);
const selectedPlusAccountAccessStrategy = getPlusAccountAccessStrategyLabel(
stateUpdates.plusAccountAccessStrategy ?? currentState?.plusAccountAccessStrategy ?? 'oauth',
stateUpdates.panelMode
?? currentState?.panelMode
?? stateUpdates.openaiIntegrationTargetId
?? currentState?.openaiIntegrationTargetId
stateUpdates.targetId
?? currentState?.targetId
?? 'cpa'
);
await addLog(
@@ -1620,10 +1614,8 @@
} else if (plusAccountAccessStrategyChanged && nextPlusModeEnabled) {
const selectedPlusAccountAccessStrategy = getPlusAccountAccessStrategyLabel(
stateUpdates.plusAccountAccessStrategy ?? currentState?.plusAccountAccessStrategy ?? 'oauth',
stateUpdates.panelMode
?? currentState?.panelMode
?? stateUpdates.openaiIntegrationTargetId
?? currentState?.openaiIntegrationTargetId
stateUpdates.targetId
?? currentState?.targetId
?? 'cpa'
);
await addLog(`Plus 账号接入策略已切换为 ${selectedPlusAccountAccessStrategy},已更新对应的 Plus 尾链。`, 'info');
@@ -1661,7 +1653,7 @@
);
const targetId = normalizeMessageTargetId(
activeFlowId,
message.payload?.targetId || currentState?.kiroTargetId || 'kiro-rs',
message.payload?.targetId || currentState?.targetId || 'kiro-rs',
'kiro-rs'
);
const nestedTargetConfig = currentState?.settingsState?.flows?.kiro?.targets?.[targetId]
+2 -3
View File
@@ -42,10 +42,10 @@
}
function getPanelMode(state = {}) {
if (state.panelMode === 'sub2api') {
if (state.targetId === 'sub2api') {
return 'sub2api';
}
if (state.panelMode === 'codex2api') {
if (state.targetId === 'codex2api') {
return 'codex2api';
}
return 'cpa';
@@ -128,7 +128,6 @@
switch (source) {
case 'openai-auth':
case 'signup-page':
return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname);
case 'duck-mail':
return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/');
+10 -10
View File
@@ -4585,7 +4585,7 @@
visibleStep,
logStepKey: 'phone-verification',
});
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'STEP8_GET_STATE',
source: 'background',
payload: { visibleStep },
@@ -4676,7 +4676,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(30000, { step: visibleStep, actionLabel: '提交添加手机号' })
: 30000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'SUBMIT_PHONE_NUMBER',
source: 'background',
payload: {
@@ -4723,7 +4723,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(45000, { step: visibleStep, actionLabel: '提交手机验证码' })
: 45000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'SUBMIT_PHONE_VERIFICATION_CODE',
source: 'background',
payload: {
@@ -4756,7 +4756,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(65000, { step: visibleStep, actionLabel: 'resend phone verification code' })
: 65000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'RESEND_PHONE_VERIFICATION_CODE',
source: 'background',
payload: options || {},
@@ -4780,7 +4780,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(45000, { step: visibleStep, actionLabel: '提交注册手机验证码' })
: 45000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'SUBMIT_PHONE_VERIFICATION_CODE',
step: visibleStep,
source: 'background',
@@ -4809,7 +4809,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(65000, { step: visibleStep, actionLabel: '重新发送注册手机验证码' })
: 65000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'RESEND_VERIFICATION_CODE',
step: visibleStep,
source: 'background',
@@ -4834,7 +4834,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(30000, { step: visibleStep, actionLabel: 'return to add-phone page' })
: 30000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'RETURN_TO_ADD_PHONE',
source: 'background',
payload: {},
@@ -4863,7 +4863,7 @@
}
const visibleStep = normalizeLogStep(activePhoneVerificationLogStep) || 9;
try {
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'CHECK_PHONE_RESEND_ERROR',
source: 'background',
payload: { visibleStep },
@@ -6135,7 +6135,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(45000, { step: visibleStep, actionLabel: '提交登录手机验证码' })
: 45000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'SUBMIT_PHONE_VERIFICATION_CODE',
step: visibleStep,
source: 'background',
@@ -6164,7 +6164,7 @@
const timeoutMs = typeof getOAuthFlowStepTimeoutMs === 'function'
? await getOAuthFlowStepTimeoutMs(65000, { step: visibleStep, actionLabel: '重新发送登录手机验证码' })
: 65000;
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'RESEND_VERIFICATION_CODE',
step: visibleStep,
source: 'background',
+16 -16
View File
@@ -26,7 +26,7 @@
setEmailState,
setState,
SIGNUP_ENTRY_URL,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
waitForTabStableComplete = null,
waitForTabUrlMatch,
} = deps;
@@ -57,16 +57,16 @@
}
async function openSignupEntryTab(step = 1) {
const tabId = await reuseOrCreateTab('signup-page', SIGNUP_ENTRY_URL, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
const tabId = await reuseOrCreateTab('openai-auth', SIGNUP_ENTRY_URL, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
});
await waitForSignupEntryTabToSettle(tabId, step);
await ensureContentScriptReadyOnTab('signup-page', tabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', tabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: `步骤 ${step}:ChatGPT 官网仍在加载,正在重试连接内容脚本...`,
@@ -77,7 +77,7 @@
async function ensureSignupEntryPageReady(step = 1) {
const tabId = await openSignupEntryTab(step);
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'ENSURE_SIGNUP_ENTRY_READY',
step,
source: 'background',
@@ -186,9 +186,9 @@
}
}
await ensureContentScriptReadyOnTab('signup-page', tabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', tabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: landingState === 'password_page'
@@ -204,7 +204,7 @@
};
}
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'ENSURE_SIGNUP_PASSWORD_PAGE_READY',
step,
source: 'background',
@@ -244,9 +244,9 @@
throw new Error(`认证页面标签页已关闭,无法完成步骤 ${step} 的提交后确认。`);
}
await ensureContentScriptReadyOnTab('signup-page', tabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', tabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: `步骤 ${step}:认证页仍在切换,正在等待页面恢复后继续确认提交流程...`,
@@ -254,7 +254,7 @@
let result;
try {
result = await sendToContentScriptResilient('signup-page', {
result = await sendToContentScriptResilient('openai-auth', {
type: 'PREPARE_SIGNUP_VERIFICATION',
step,
source: 'background',
+8 -8
View File
@@ -242,7 +242,7 @@
const requestTimeoutMs = Math.max(1200, Math.min(5000, timeoutMs));
const result = typeof sendToContentScriptResilient === 'function'
? await sendToContentScriptResilient(
'signup-page',
'openai-auth',
request,
{
timeoutMs: requestTimeoutMs,
@@ -251,7 +251,7 @@
logMessage: `步骤 ${step}:验证码提交后页面正在切换,等待页面恢复并确认授权状态...`,
}
)
: await sendToContentScript('signup-page', request, {
: await sendToContentScript('openai-auth', request, {
responseTimeoutMs: requestTimeoutMs,
});
@@ -516,7 +516,7 @@
async function requestVerificationCodeResend(step, options = {}) {
throwIfStopped();
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
if (!signupTabId) {
throw new Error('认证页面标签页已关闭,无法重新请求验证码。');
}
@@ -525,7 +525,7 @@
await chrome.tabs.update(signupTabId, { active: true });
throwIfStopped();
const result = await sendToContentScript('signup-page', {
const result = await sendToContentScript('openai-auth', {
type: 'RESEND_VERIFICATION_CODE',
step,
source: 'background',
@@ -1111,7 +1111,7 @@
async function submitVerificationCode(step, code, options = {}) {
const completionStep = getCompletionStep(step, options);
const authLoginStep = completionStep >= 11 ? 10 : 7;
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
if (!signupTabId) {
throw new Error('认证页面标签页已关闭,无法填写验证码。');
}
@@ -1143,7 +1143,7 @@
const shouldAvoidReplaySubmit = step === 8;
if (typeof sendToContentScriptResilient === 'function' && !shouldAvoidReplaySubmit) {
try {
result = await sendToContentScriptResilient('signup-page', message, {
result = await sendToContentScriptResilient('openai-auth', message, {
timeoutMs: Math.max(baseResponseTimeoutMs + 15000, 30000),
retryDelayMs: 700,
responseTimeoutMs: baseResponseTimeoutMs,
@@ -1206,7 +1206,7 @@
}
} else if (shouldAvoidReplaySubmit) {
try {
result = await sendToContentScript('signup-page', message, {
result = await sendToContentScript('openai-auth', message, {
responseTimeoutMs: baseResponseTimeoutMs,
});
} catch (err) {
@@ -1255,7 +1255,7 @@
throw err;
}
} else {
result = await sendToContentScript('signup-page', message, {
result = await sendToContentScript('openai-auth', message, {
responseTimeoutMs: baseResponseTimeoutMs,
});
}
@@ -12,9 +12,11 @@
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION = 'cpa_codex_session';
const VALID_OPENAI_TARGET_IDS = Array.isArray(flowRegistryApi.OPENAI_TARGET_IDS)
? flowRegistryApi.OPENAI_TARGET_IDS.slice()
: ['cpa', 'sub2api', 'codex2api'];
const VALID_OPENAI_TARGET_IDS = typeof flowRegistryApi.getTargetDefinitions === 'function'
? Object.keys(flowRegistryApi.getTargetDefinitions('openai') || {})
: (Array.isArray(flowRegistryApi.OPENAI_TARGET_IDS)
? flowRegistryApi.OPENAI_TARGET_IDS.slice()
: ['cpa', 'sub2api', 'codex2api']);
const REGISTERED_FLOW_IDS = Array.isArray(flowRegistryApi.getRegisteredFlowIds?.())
? flowRegistryApi.getRegisteredFlowIds().map((flowId) => String(flowId || '').trim().toLowerCase()).filter(Boolean)
: [DEFAULT_FLOW_ID];
@@ -63,38 +65,18 @@
const MODE_SWITCH_RELEVANT_KEYS = Object.freeze([
'activeFlowId',
'accountContributionEnabled',
'panelMode',
'phoneVerificationEnabled',
'plusModeEnabled',
'signupMethod',
'plusAccountAccessStrategy',
'openaiIntegrationTargetId',
'kiroTargetId',
'targetId',
]);
const OPENAI_TARGET_CAPABILITIES = Object.freeze({
cpa: Object.freeze({
supportsPhoneSignup: true,
requiresPhoneSignupWarning: true,
supportedPlusAccountAccessStrategies: Object.freeze([
PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH,
PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION,
]),
}),
sub2api: Object.freeze({
supportsPhoneSignup: true,
requiresPhoneSignupWarning: false,
supportedPlusAccountAccessStrategies: Object.freeze([
PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH,
PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION,
]),
}),
codex2api: Object.freeze({
supportsPhoneSignup: true,
requiresPhoneSignupWarning: false,
supportedPlusAccountAccessStrategies: Object.freeze([PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH]),
}),
});
const OPENAI_TARGET_CAPABILITIES = Object.freeze(
typeof flowRegistryApi.getTargetCapabilityDefinitions === 'function'
? (flowRegistryApi.getTargetCapabilityDefinitions('openai') || {})
: {}
);
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
if (typeof flowRegistryApi.normalizeFlowId === 'function') {
@@ -254,33 +236,18 @@
}
function normalizeRequestedTargetId(activeFlowId, state = {}, options = {}) {
if (activeFlowId === 'openai') {
return normalizeOpenAiTargetId(
options?.targetId
?? options?.integrationTargetId
?? options?.panelMode
?? state?.openaiIntegrationTargetId
?? state?.panelMode,
DEFAULT_OPENAI_TARGET_ID
);
}
const rawTargetId = activeFlowId === 'kiro'
? (
options?.targetId
?? state?.kiroTargetId
?? flowRegistryApi.getDefaultTargetId?.(activeFlowId)
?? ''
)
: (
options?.targetId
?? state?.targetId
?? state?.openaiIntegrationTargetId
?? state?.panelMode
?? state?.kiroTargetId
?? flowRegistryApi.getDefaultTargetId?.(activeFlowId)
?? ''
);
const schemaTargetId = settingsSchema?.getSelectedTargetId
? settingsSchema.getSelectedTargetId({
...state,
activeFlowId,
}, activeFlowId)
: '';
const rawTargetId = options?.targetId
?? options?.selectedTargetId
?? state?.targetId
?? schemaTargetId
?? flowRegistryApi.getDefaultTargetId?.(activeFlowId)
?? '';
if (
isRegisteredFlowId(activeFlowId)
@@ -312,32 +279,9 @@
}
function resolveEffectiveTargetId(activeFlowId, state = {}, requestedTargetId = DEFAULT_OPENAI_TARGET_ID) {
if (!isRegisteredFlowId(activeFlowId)) {
return normalizeRequestedTargetId(activeFlowId, state, {
targetId: requestedTargetId,
});
}
if (settingsSchema?.getSelectedTargetId) {
const targetId = settingsSchema.getSelectedTargetId({
...state,
activeFlowId,
}, activeFlowId);
if (targetId) {
return targetId;
}
}
if (typeof flowRegistryApi.normalizeTargetId === 'function') {
return flowRegistryApi.normalizeTargetId(
activeFlowId,
activeFlowId === 'openai'
? (state?.openaiIntegrationTargetId || state?.panelMode || requestedTargetId)
: (state?.kiroTargetId || requestedTargetId),
flowRegistryApi.getDefaultTargetId?.(activeFlowId)
);
}
return activeFlowId === 'openai'
? normalizeOpenAiTargetId(requestedTargetId)
: String(requestedTargetId || '').trim().toLowerCase();
return normalizeRequestedTargetId(activeFlowId, state, {
targetId: requestedTargetId,
});
}
function resolveSidepanelCapabilities(options = {}) {
@@ -444,13 +388,11 @@
canUsePhoneSignup: canSelectPhoneSignup,
canUseSelectedTarget: targetSupported,
effectivePlusAccountAccessStrategy,
effectivePanelMode: effectiveTargetId,
effectiveSignupMethod,
effectiveSignupMethods,
effectiveTargetId,
flowCapabilities: flowState,
panelCapabilities: targetState,
panelMode: effectiveTargetId,
requestedPlusAccountAccessStrategy,
requestedSignupMethod,
requestedTargetId,
@@ -460,8 +402,6 @@
&& Boolean(targetState.requiresPhoneSignupWarning),
stepDefinitionOptions: {
activeFlowId,
integrationTargetId: effectiveTargetId,
panelMode: effectiveTargetId,
targetId: effectiveTargetId,
plusAccountAccessStrategy: effectivePlusAccountAccessStrategy,
plusModeEnabled: runtimeLocks.plusModeEnabled,
@@ -576,14 +516,12 @@
const shouldReconcileSignupMethod = MODE_SWITCH_RELEVANT_KEYS.some((key) => changedKeySet.has(key));
if (
(changedKeySet.has('panelMode') || changedKeySet.has('openaiIntegrationTargetId') || changedKeySet.has('kiroTargetId'))
changedKeySet.has('targetId')
&& Array.isArray(capabilityState.supportedTargetIds)
&& capabilityState.supportedTargetIds.length > 0
&& capabilityState.canUseSelectedTarget === false
) {
normalizedUpdates.panelMode = capabilityState.effectiveTargetId;
normalizedUpdates.openaiIntegrationTargetId = capabilityState.effectiveTargetId;
normalizedUpdates.kiroTargetId = capabilityState.effectiveTargetId;
normalizedUpdates.targetId = capabilityState.effectiveTargetId;
errors.push({
code: 'panel_mode_unsupported',
message: `当前 flow 不支持 ${getTargetLabel(capabilityState.activeFlowId, capabilityState.requestedTargetId)} 来源。`,
+300
View File
@@ -0,0 +1,300 @@
(function attachMultiPageFlowRegistry(root, factory) {
root.MultiPageFlowRegistry = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createFlowRegistryModule() {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
const flowsIndexApi = rootScope.MultiPageFlowsIndex || {};
const DEFAULT_FLOW_ID = 'openai';
const SHARED_SERVICE_IDS = Object.freeze(['account', 'email', 'proxy']);
const DEFAULT_FLOW_CAPABILITIES = Object.freeze({
supportsEmailSignup: true,
supportsPhoneSignup: false,
supportsPhoneVerificationSettings: false,
supportsPlusMode: false,
supportsContributionMode: false,
supportsAccountContribution: false,
supportsOpenAiOAuthContribution: false,
contributionAdapterIds: [],
supportedTargetIds: [],
supportsLuckmail: false,
supportsOauthTimeoutBudget: false,
canSwitchFlow: true,
stepDefinitionMode: 'default',
targetSelectorLabel: '\u6765\u6e90',
});
function freezeDeep(value) {
if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
return value;
}
Object.getOwnPropertyNames(value).forEach((key) => {
freezeDeep(value[key]);
});
return Object.freeze(value);
}
const SHARED_SETTINGS_GROUP_DEFINITIONS = freezeDeep({
'service-account': {
id: 'service-account',
label: '\u8d26\u6237',
rowIds: ['row-custom-password'],
},
'service-email': {
id: 'service-email',
label: '\u90ae\u7bb1\u670d\u52a1',
},
'service-proxy': {
id: 'service-proxy',
label: 'IP \u4ee3\u7406',
sectionIds: ['ip-proxy-section'],
},
});
function buildFlowDefinitions() {
if (typeof flowsIndexApi.getFlowDefinitions !== 'function') {
return {};
}
return flowsIndexApi.getFlowDefinitions() || {};
}
const FLOW_DEFINITIONS = freezeDeep(buildFlowDefinitions());
const REGISTERED_FLOW_IDS = Object.freeze(Object.keys(FLOW_DEFINITIONS));
function buildSettingsGroupDefinitions() {
const next = {
...SHARED_SETTINGS_GROUP_DEFINITIONS,
};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition?.settingsGroups || {});
});
return next;
}
const SETTINGS_GROUP_DEFINITIONS = freezeDeep(buildSettingsGroupDefinitions());
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
const normalized = String(value || '').trim().toLowerCase();
if (normalized && Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, normalized)) {
return normalized;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
if (fallbackValue && Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, fallbackValue)) {
return fallbackValue;
}
if (Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, DEFAULT_FLOW_ID)) {
return DEFAULT_FLOW_ID;
}
return REGISTERED_FLOW_IDS[0] || DEFAULT_FLOW_ID;
}
function getRegisteredFlowIds() {
return REGISTERED_FLOW_IDS.slice();
}
function getFlowDefinition(flowId) {
const normalizedFlowId = normalizeFlowId(flowId);
return FLOW_DEFINITIONS[normalizedFlowId] || null;
}
function getFlowLabel(flowId) {
return getFlowDefinition(flowId)?.label || normalizeFlowId(flowId);
}
function getDefaultTargetId(flowId) {
const flowDefinition = getFlowDefinition(flowId);
return String(flowDefinition?.defaultTargetId || '').trim().toLowerCase();
}
function normalizeTargetId(flowId, targetId = '', fallback = undefined) {
const normalizedFlowId = normalizeFlowId(flowId);
const targetDefinitions = getFlowDefinition(normalizedFlowId)?.targets || {};
const targetKeys = Object.keys(targetDefinitions);
if (!targetKeys.length) {
return String(targetId || fallback || '').trim().toLowerCase();
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
if (normalizedTargetId && Object.prototype.hasOwnProperty.call(targetDefinitions, normalizedTargetId)) {
return normalizedTargetId;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
if (fallbackValue && Object.prototype.hasOwnProperty.call(targetDefinitions, fallbackValue)) {
return fallbackValue;
}
const defaultTargetId = getDefaultTargetId(normalizedFlowId);
if (defaultTargetId && Object.prototype.hasOwnProperty.call(targetDefinitions, defaultTargetId)) {
return defaultTargetId;
}
return targetKeys[0];
}
function normalizeOpenAiTargetId(value = '', fallback = undefined) {
return normalizeTargetId('openai', value, fallback);
}
function normalizeKiroTargetId(value = '', fallback = undefined) {
return normalizeTargetId('kiro', value, fallback);
}
function getTargetDefinitions(flowId) {
return getFlowDefinition(flowId)?.targets || {};
}
function getTargetDefinition(flowId, targetId) {
const normalizedFlowId = normalizeFlowId(flowId);
const normalizedTargetId = normalizeTargetId(
normalizedFlowId,
targetId,
getDefaultTargetId(normalizedFlowId)
);
return getTargetDefinitions(normalizedFlowId)[normalizedTargetId] || null;
}
function getTargetOptions(flowId) {
return Object.values(getTargetDefinitions(flowId));
}
function getTargetLabel(flowId, targetId) {
return getTargetDefinition(flowId, targetId)?.label
|| normalizeTargetId(flowId, targetId);
}
function getPublicationTargetDefinitions(flowId) {
return getFlowDefinition(flowId)?.publicationTargets || {};
}
function getPublicationTargetDefinition(flowId, publicationTargetId) {
const normalizedFlowId = normalizeFlowId(flowId);
const flowDefinition = getFlowDefinition(normalizedFlowId);
const normalizedPublicationTargetId = String(
publicationTargetId || flowDefinition?.defaultPublicationTargetId || ''
).trim().toLowerCase();
return getPublicationTargetDefinitions(normalizedFlowId)[normalizedPublicationTargetId] || null;
}
function getFlowCapabilities(flowId) {
return {
...DEFAULT_FLOW_CAPABILITIES,
...(getFlowDefinition(flowId)?.capabilities || {}),
};
}
function getTargetCapabilityDefinitions(flowId) {
return getFlowDefinition(flowId)?.targetCapabilities || {};
}
function getTargetCapabilities(flowId, targetId) {
const normalizedFlowId = normalizeFlowId(flowId);
const normalizedTargetId = normalizeTargetId(
normalizedFlowId,
targetId,
getDefaultTargetId(normalizedFlowId)
);
return getTargetCapabilityDefinitions(normalizedFlowId)[normalizedTargetId] || null;
}
function getVisibleGroupIds(flowId, targetId, options = {}) {
const normalizedFlowId = normalizeFlowId(flowId);
const flowDefinition = getFlowDefinition(normalizedFlowId);
const normalizedTargetId = normalizeTargetId(
normalizedFlowId,
targetId,
getDefaultTargetId(normalizedFlowId)
);
const targetDefinition = getTargetDefinition(normalizedFlowId, normalizedTargetId);
const includeSharedServices = options?.includeSharedServices !== false;
const serviceGroups = includeSharedServices
? (Array.isArray(flowDefinition?.services)
? flowDefinition.services.map((serviceId) => `service-${serviceId}`)
: [])
: [];
return Array.from(new Set([
...(Array.isArray(flowDefinition?.baseGroups) ? flowDefinition.baseGroups : []),
...(Array.isArray(targetDefinition?.groups) ? targetDefinition.groups : []),
...serviceGroups,
]));
}
function getSettingsGroupDefinition(groupId) {
const normalizedGroupId = String(groupId || '').trim();
return SETTINGS_GROUP_DEFINITIONS[normalizedGroupId] || null;
}
function getSettingsGroupDefinitions() {
return SETTINGS_GROUP_DEFINITIONS;
}
function getRuntimeSourceDefinitions() {
const next = {};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition?.runtimeSources || {});
});
return next;
}
function getDriverDefinitions() {
const next = {};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition?.driverDefinitions || {});
});
return next;
}
function getSourceAliases() {
const next = {};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition?.sourceAliases || {});
});
return next;
}
const OPENAI_TARGET_IDS = Object.freeze(Object.keys(getTargetDefinitions('openai')));
const DEFAULT_OPENAI_TARGET_ID = String(getDefaultTargetId('openai') || OPENAI_TARGET_IDS[0] || 'cpa');
const DEFAULT_KIRO_TARGET_ID = String(getDefaultTargetId('kiro') || 'kiro-rs');
const DEFAULT_KIRO_PUBLICATION_TARGET_ID = String(
getFlowDefinition('kiro')?.defaultPublicationTargetId || DEFAULT_KIRO_TARGET_ID
);
const DEFAULT_KIRO_RS_URL = String(
getFlowDefinition('kiro')?.defaultTargetState?.baseUrl || ''
).trim();
return {
DEFAULT_FLOW_CAPABILITIES,
DEFAULT_FLOW_ID,
DEFAULT_KIRO_PUBLICATION_TARGET_ID,
DEFAULT_KIRO_RS_URL,
DEFAULT_KIRO_TARGET_ID,
DEFAULT_OPENAI_TARGET_ID,
FLOW_DEFINITIONS,
OPENAI_TARGET_IDS,
SETTINGS_GROUP_DEFINITIONS,
SHARED_SERVICE_IDS,
getDriverDefinitions,
getDefaultTargetId,
getFlowCapabilities,
getFlowDefinition,
getFlowLabel,
getPublicationTargetDefinition,
getPublicationTargetDefinitions,
getRegisteredFlowIds,
getRuntimeSourceDefinitions,
getSettingsGroupDefinition,
getSettingsGroupDefinitions,
getSourceAliases,
getTargetCapabilities,
getTargetCapabilityDefinitions,
getTargetDefinition,
getTargetDefinitions,
getTargetLabel,
getTargetOptions,
getVisibleGroupIds,
normalizeFlowId,
normalizeKiroTargetId,
normalizeOpenAiTargetId,
normalizeTargetId,
};
});
+257
View File
@@ -0,0 +1,257 @@
(function attachBackgroundLoggingStatus(root, factory) {
root.MultiPageBackgroundLoggingStatus = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundLoggingStatusModule() {
function createLoggingStatus(deps = {}) {
const {
chrome,
DEFAULT_STATE,
getStepIdByNodeIdForState,
getState,
isRecoverableStep9AuthFailure,
LOG_PREFIX,
setState,
sourceRegistry = null,
STOP_ERROR_MESSAGE,
} = deps;
function getSourceLabel(source) {
if (sourceRegistry?.getSourceLabel) {
return sourceRegistry.getSourceLabel(source);
}
const labels = {
'openai-auth': '认证页',
'gmail-mail': 'Gmail 邮箱',
'sidepanel': '侧边栏',
'vps-panel': 'CPA 面板',
'sub2api-panel': 'SUB2API 后台',
'codex2api-panel': 'Codex2API 后台',
'qq-mail': 'QQ 邮箱',
'mail-163': '163 邮箱',
'mail-2925': '2925 邮箱',
'inbucket-mail': 'Inbucket 邮箱',
'duck-mail': 'Duck 邮箱',
'hotmail-api': 'HotmailAPI对接/本地助手)',
'luckmail-api': 'LuckMailAPI 购邮)',
'cloudflare-temp-email': 'Cloudflare Temp Email',
'cloudmail': 'Cloud Mail',
'plus-checkout': 'Plus Checkout',
'paypal-flow': 'PayPal 授权页',
'gopay-flow': 'GoPay 授权页',
'unknown-source': '未知来源',
};
return labels[source] || source || '未知来源';
}
function normalizeLogStep(value) {
const step = Math.floor(Number(value) || 0);
return step > 0 ? step : null;
}
function buildLogEntry(message, level = 'info', options = {}) {
const normalizedOptions = options && typeof options === 'object' ? options : {};
const step = normalizeLogStep(normalizedOptions.step);
const stepKey = String(normalizedOptions.stepKey || '').trim();
const nodeId = String(normalizedOptions.nodeId || normalizedOptions.nodeKey || stepKey || '').trim();
return {
message: String(message || ''),
level,
timestamp: Date.now(),
step,
stepKey,
nodeId,
};
}
async function addLog(message, level = 'info', options = {}) {
const state = await getState();
const logs = state.logs || [];
const entry = buildLogEntry(message, level, options);
logs.push(entry);
if (logs.length > 500) logs.splice(0, logs.length - 500);
await setState({ logs });
chrome.runtime.sendMessage({ type: 'LOG_ENTRY', payload: entry }).catch(() => { });
}
async function setNodeStatus(nodeId, status) {
const normalizedNodeId = String(nodeId || '').trim();
if (!normalizedNodeId) {
throw new Error('setNodeStatus 缺少 nodeId。');
}
const state = await getState();
const nodeStatuses = { ...(state.nodeStatuses || {}) };
nodeStatuses[normalizedNodeId] = status;
await setState({
nodeStatuses,
currentNodeId: normalizedNodeId,
});
chrome.runtime.sendMessage({
type: 'NODE_STATUS_CHANGED',
payload: { nodeId: normalizedNodeId, status },
}).catch(() => { });
}
function getErrorMessage(error) {
return String(typeof error === 'string' ? error : error?.message || '')
.replace(/^GPC_TASK_ENDED::/i, '')
.replace(/^AUTO_RUN_STEP_IDLE_RESTART::/i, '');
}
function isVerificationMailPollingError(error) {
const message = getErrorMessage(error);
return /未在 .*邮箱中找到新的匹配邮件|未在 Hotmail 收件箱中找到新的匹配验证码|邮箱轮询结束,但未获取到验证码|无法获取新的(?:注册|登录)验证码|页面未能重新就绪|页面通信异常|内容脚本\s+\d+(?:\.\d+)?\s*秒内未响应|did not respond in \d+s|405\s+method\s+not\s+allowed|route\s+error.*405|did\s+not\s+provide\s+an?\s+[`'"]?action|post\s+request\s+to\s+["']?\/(?:email|phone)-verification/i.test(message);
}
function isAddPhoneAuthFailure(error) {
const message = getErrorMessage(error);
if (/\u624b\u673a\u53f7\u8f93\u5165\u6a21\u5f0f|phone\s+entry/i.test(message)) {
return false;
}
return /https:\/\/auth\.openai\.com\/(?:add-phone|phone-verification)(?:[/?#]|$)|\badd-phone\b|phone-verification|\u6dfb\u52a0\u624b\u673a\u53f7|\u624b\u673a\u53f7\u7801|\u624b\u673a\u9a8c\u8bc1\u7801\u9875|\u624b\u673a\u9a8c\u8bc1\u9875|\u8fdb\u5165\u624b\u673a\u53f7\u9875\u9762|\u624b\u673a\u53f7\u9875|\u624b\u673a\u53f7\u9875\u9762|phone\s+number|telephone/i.test(message);
}
function getLoginAuthStateLabel(state) {
switch (state) {
case 'verification_page':
return '登录验证码页';
case 'password_page':
return '密码页';
case 'email_page':
return '邮箱输入页';
case 'login_timeout_error_page':
return '登录超时报错页';
case 'oauth_consent_page':
return 'OAuth 授权页';
case 'add_phone_page':
return '手机号页';
case 'add_email_page':
return '添加邮箱页';
case 'phone_verification_page':
return '手机验证码页';
default:
return '未知页面';
}
}
function isRestartCurrentAttemptError(error) {
const message = String(typeof error === 'string' ? error : error?.message || '');
return /当前邮箱已存在,需要重新开始新一轮|SIGNUP_PHONE_PASSWORD_MISMATCH::/i.test(message);
}
function isSignupUserAlreadyExistsFailure(error) {
const message = getErrorMessage(error);
return /SIGNUP_USER_ALREADY_EXISTS::|user_already_exists/i.test(message);
}
function isKiroProxyFailure(error) {
const message = getErrorMessage(error);
return /Kiro\s*(?:注册页|桌面授权页).*(?:CloudFront\s*拒绝请求|AWS\s*请求异常)|(?:当前代理\s*IP|出口区域异常).*(?:切换代理|更换代理)|AWS\s*风控.*(?:切换代理|更换代理)/i.test(message);
}
function isStep9RecoverableAuthError(error) {
const message = String(typeof error === 'string' ? error : error?.message || '');
return /STEP9_OAUTH_RETRY::/i.test(message)
|| isRecoverableStep9AuthFailure(message);
}
function isLegacyStep9RecoverableAuthError(error) {
const message = String(typeof error === 'string' ? error : error?.message || '');
return /STEP9_OAUTH_TIMEOUT::|认证失败:\s*(?:Timeout waiting for OAuth callback|timeout of \d+ms exceeded)/i.test(message);
}
function isStepDoneStatus(status) {
return status === 'completed' || status === 'manual_completed' || status === 'skipped';
}
function getFirstUnfinishedStep(statuses = {}) {
const nodeStatuses = statuses && typeof statuses === 'object' ? statuses : {};
const nodeIds = Object.keys(DEFAULT_STATE.nodeStatuses || {});
for (const nodeId of nodeIds) {
if (!isStepDoneStatus(nodeStatuses[nodeId] || 'pending')) {
return typeof getStepIdByNodeIdForState === 'function'
? getStepIdByNodeIdForState(nodeId, {})
: null;
}
}
return null;
}
function hasSavedProgress(statuses = {}) {
return Object.values({ ...DEFAULT_STATE.nodeStatuses, ...statuses }).some((status) => status !== 'pending');
}
function getRunningSteps(statuses = {}) {
return Object.entries({ ...DEFAULT_STATE.nodeStatuses, ...statuses })
.filter(([, status]) => status === 'running')
.map(([nodeId]) => (typeof getStepIdByNodeIdForState === 'function' ? getStepIdByNodeIdForState(nodeId, {}) : null))
.filter((step) => Number.isInteger(step) && step > 0)
.sort((a, b) => a - b);
}
function getFirstUnfinishedNode(statuses = {}) {
const nodeIds = Object.keys(DEFAULT_STATE.nodeStatuses || {});
for (const nodeId of nodeIds) {
if (!isStepDoneStatus(statuses[nodeId] || 'pending')) {
return nodeId;
}
}
return '';
}
function hasSavedNodeProgress(statuses = {}) {
return Object.values({ ...DEFAULT_STATE.nodeStatuses, ...statuses }).some((status) => status !== 'pending');
}
function getRunningNodes(statuses = {}) {
return Object.entries({ ...DEFAULT_STATE.nodeStatuses, ...statuses })
.filter(([, status]) => status === 'running')
.map(([nodeId]) => nodeId);
}
function getAutoRunStatusPayload(phase, payload = {}) {
return {
autoRunning: phase === 'scheduled'
|| phase === 'running'
|| phase === 'waiting_step'
|| phase === 'waiting_email'
|| phase === 'retrying'
|| phase === 'waiting_interval',
autoRunPhase: phase,
autoRunCurrentRun: payload.currentRun ?? 0,
autoRunTotalRuns: payload.totalRuns ?? 1,
autoRunAttemptRun: payload.attemptRun ?? 0,
autoRunSessionId: Math.max(0, Math.floor(Number(payload.sessionId ?? payload.autoRunSessionId) || 0)),
scheduledAutoRunAt: Number.isFinite(Number(payload.scheduledAt)) ? Number(payload.scheduledAt) : null,
autoRunCountdownAt: Number.isFinite(Number(payload.countdownAt)) ? Number(payload.countdownAt) : null,
autoRunCountdownTitle: payload.countdownTitle === undefined ? '' : String(payload.countdownTitle || ''),
autoRunCountdownNote: payload.countdownNote === undefined ? '' : String(payload.countdownNote || ''),
};
}
return {
addLog,
getAutoRunStatusPayload,
getFirstUnfinishedNode,
isAddPhoneAuthFailure,
getErrorMessage,
getFirstUnfinishedStep,
getLoginAuthStateLabel,
getRunningNodes,
getRunningSteps,
getSourceLabel,
hasSavedNodeProgress,
hasSavedProgress,
isKiroProxyFailure,
isLegacyStep9RecoverableAuthError,
isRestartCurrentAttemptError,
isSignupUserAlreadyExistsFailure,
isStep9RecoverableAuthError,
isStepDoneStatus,
isVerificationMailPollingError,
setNodeStatus,
};
}
return {
createLoggingStatus,
};
});
@@ -140,6 +140,24 @@
return value;
}
function deepMerge(baseValue, patchValue) {
if (Array.isArray(patchValue)) {
return patchValue.map((item) => cloneValue(item));
}
if (!isPlainObject(patchValue)) {
return patchValue === undefined ? cloneValue(baseValue) : patchValue;
}
const baseObject = isPlainObject(baseValue) ? baseValue : {};
const next = {
...cloneValue(baseObject),
};
Object.entries(patchValue).forEach(([key, value]) => {
next[key] = deepMerge(baseObject[key], value);
});
return next;
}
function normalizePlainObject(value) {
return isPlainObject(value) ? value : {};
}
@@ -277,6 +295,7 @@
'activeRunId',
'currentNodeId',
'nodeStatuses',
'kiroRuntime',
...RUNTIME_TOP_LEVEL_FIELDS,
]);
@@ -358,6 +377,43 @@
return next;
}
function buildRuntimeStateSeed(currentRuntimeState = {}, updates = {}) {
const normalizedUpdates = normalizePlainObject(updates);
let nextRuntimeState = deepMerge(currentRuntimeState, normalizePlainObject(normalizedUpdates.runtimeState));
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'sharedState')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
sharedState: normalizePlainObject(normalizedUpdates.sharedState),
});
}
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'shared')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
sharedState: normalizePlainObject(normalizedUpdates.shared),
});
}
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'serviceState')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
serviceState: normalizePlainObject(normalizedUpdates.serviceState),
});
}
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'services')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
serviceState: normalizePlainObject(normalizedUpdates.services),
});
}
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'flowState')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
flowState: normalizePlainObject(normalizedUpdates.flowState),
});
}
if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'flows')) {
nextRuntimeState = deepMerge(nextRuntimeState, {
flowState: normalizePlainObject(normalizedUpdates.flows),
});
}
return nextRuntimeState;
}
function buildRuntimeStateDefault() {
return {
flowId: DEFAULT_ACTIVE_FLOW_ID,
@@ -388,6 +444,19 @@
...buildRuntimeStateDefault(),
...cloneValue(normalizePlainObject(state.runtimeState)),
};
const projectedKiroRuntime = isPlainObject(normalizePlainObject(state.flowState).kiro)
? normalizePlainObject(state.flowState.kiro)
: {};
const canonicalKiroRuntime = deepMerge(
normalizePlainObject(normalizePlainObject(baseRuntimeState.flowState).kiro),
projectedKiroRuntime
);
if (Object.keys(canonicalKiroRuntime).length > 0) {
baseRuntimeState.flowState = {
...cloneValue(normalizePlainObject(baseRuntimeState.flowState)),
kiro: canonicalKiroRuntime,
};
}
const activeFlowId = normalizeFlowId(
Object.prototype.hasOwnProperty.call(state, 'activeFlowId')
? state.activeFlowId
@@ -472,9 +541,10 @@
function buildSessionStatePatch(currentState = {}, updates = {}) {
const currentRuntimeState = ensureRuntimeState(currentState);
const runtimeStateSeed = buildRuntimeStateSeed(currentRuntimeState, updates);
const runtimeState = ensureRuntimeState({
runtimeState: currentRuntimeState,
...projectRuntimeViewFields(currentRuntimeState),
runtimeState: runtimeStateSeed,
...projectRuntimeViewFields(runtimeStateSeed),
...buildRuntimeInputFromPatch(updates),
});
@@ -60,7 +60,7 @@
function buildDefaultSettingsState() {
return {
schemaVersion: 4,
schemaVersion: 5,
activeFlowId: defaultFlowId,
services: {
account: {
@@ -77,8 +77,8 @@
},
flows: {
openai: {
integrationTargetId: defaultOpenAiTargetId,
integrationTargets: {
selectedTargetId: defaultOpenAiTargetId,
targets: {
cpa: {
vpsUrl: '',
vpsPassword: '',
@@ -120,7 +120,7 @@
},
},
kiro: {
targetId: defaultKiroTargetId,
selectedTargetId: defaultKiroTargetId,
targets: {
'kiro-rs': {
baseUrl: defaultKiroRsUrl,
@@ -139,8 +139,12 @@
};
}
function getIntegrationTargetValue(settingsState, pathGetter, fallback = {}) {
return cloneValue(pathGetter(isPlainObject(settingsState) ? settingsState : {}) || fallback);
function getTargetValue(settingsState, pathGetter, legacyPathGetter = null, fallback = {}) {
const sourceState = isPlainObject(settingsState) ? settingsState : {};
const resolvedValue = pathGetter(sourceState)
|| (typeof legacyPathGetter === 'function' ? legacyPathGetter(sourceState) : null)
|| fallback;
return cloneValue(resolvedValue);
}
function normalizeSettingsState(input = {}, options = {}) {
@@ -155,20 +159,25 @@
?? defaults.activeFlowId,
defaults.activeFlowId
);
const openaiIntegrationTargetId = normalizeTargetId(
const openaiSelectedTargetId = normalizeTargetId(
'openai',
nested?.flows?.openai?.integrationTargetId
?? input?.openaiIntegrationTargetId
?? input?.panelMode
?? defaults.flows.openai.integrationTargetId,
defaults.flows.openai.integrationTargetId
nested?.flows?.openai?.selectedTargetId
?? nested?.flows?.openai?.integrationTargetId
?? (activeFlowId === 'openai'
? (input?.selectedTargetId ?? input?.targetId)
: undefined)
?? defaults.flows.openai.selectedTargetId,
defaults.flows.openai.selectedTargetId
);
const kiroTargetId = normalizeTargetId(
const kiroSelectedTargetId = normalizeTargetId(
'kiro',
nested?.flows?.kiro?.targetId
?? input?.kiroTargetId
?? defaults.flows.kiro.targetId,
defaults.flows.kiro.targetId
nested?.flows?.kiro?.selectedTargetId
?? nested?.flows?.kiro?.targetId
?? (activeFlowId === 'kiro'
? (input?.selectedTargetId ?? input?.targetId)
: undefined)
?? defaults.flows.kiro.selectedTargetId,
defaults.flows.kiro.selectedTargetId
);
const stepExecutionRangeByFlow = isPlainObject(input?.stepExecutionRangeByFlow)
? input.stepExecutionRangeByFlow
@@ -212,76 +221,101 @@
},
flows: {
openai: {
integrationTargetId: openaiIntegrationTargetId,
integrationTargets: {
selectedTargetId: openaiSelectedTargetId,
targets: {
cpa: {
...defaults.flows.openai.integrationTargets.cpa,
...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.cpa),
...defaults.flows.openai.targets.cpa,
...getTargetValue(
nested,
(state) => state.flows?.openai?.targets?.cpa,
(state) => state.flows?.openai?.integrationTargets?.cpa
),
vpsUrl: String(
input?.vpsUrl
?? nested?.flows?.openai?.targets?.cpa?.vpsUrl
?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsUrl
?? ''
).trim(),
vpsPassword: String(
input?.vpsPassword
?? nested?.flows?.openai?.targets?.cpa?.vpsPassword
?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsPassword
?? ''
),
localCpaStep9Mode: String(
input?.localCpaStep9Mode
?? nested?.flows?.openai?.targets?.cpa?.localCpaStep9Mode
?? nested?.flows?.openai?.integrationTargets?.cpa?.localCpaStep9Mode
?? defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode
).trim() || defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode,
?? defaults.flows.openai.targets.cpa.localCpaStep9Mode
).trim() || defaults.flows.openai.targets.cpa.localCpaStep9Mode,
},
sub2api: {
...defaults.flows.openai.integrationTargets.sub2api,
...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.sub2api),
...defaults.flows.openai.targets.sub2api,
...getTargetValue(
nested,
(state) => state.flows?.openai?.targets?.sub2api,
(state) => state.flows?.openai?.integrationTargets?.sub2api
),
sub2apiUrl: String(
input?.sub2apiUrl
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiUrl
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiUrl
?? ''
).trim(),
sub2apiEmail: String(
input?.sub2apiEmail
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiEmail
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiEmail
?? ''
).trim(),
sub2apiPassword: String(
input?.sub2apiPassword
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiPassword
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiPassword
?? ''
),
sub2apiGroupName: String(
input?.sub2apiGroupName
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiGroupName
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiGroupName
?? defaults.flows.openai.integrationTargets.sub2api.sub2apiGroupName
).trim() || defaults.flows.openai.integrationTargets.sub2api.sub2apiGroupName,
?? defaults.flows.openai.targets.sub2api.sub2apiGroupName
).trim() || defaults.flows.openai.targets.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]),
: (Array.isArray(nested?.flows?.openai?.targets?.sub2api?.sub2apiGroupNames)
? nested.flows.openai.targets.sub2api.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.targets.sub2api.sub2apiGroupNames])),
sub2apiAccountPriority: Math.max(1, Number(
input?.sub2apiAccountPriority
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiAccountPriority
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiAccountPriority
?? defaults.flows.openai.integrationTargets.sub2api.sub2apiAccountPriority
) || defaults.flows.openai.integrationTargets.sub2api.sub2apiAccountPriority),
?? defaults.flows.openai.targets.sub2api.sub2apiAccountPriority
) || defaults.flows.openai.targets.sub2api.sub2apiAccountPriority),
sub2apiDefaultProxyName: String(
input?.sub2apiDefaultProxyName
?? nested?.flows?.openai?.targets?.sub2api?.sub2apiDefaultProxyName
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiDefaultProxyName
?? ''
).trim(),
},
codex2api: {
...defaults.flows.openai.integrationTargets.codex2api,
...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.codex2api),
...defaults.flows.openai.targets.codex2api,
...getTargetValue(
nested,
(state) => state.flows?.openai?.targets?.codex2api,
(state) => state.flows?.openai?.integrationTargets?.codex2api
),
codex2apiUrl: String(
input?.codex2apiUrl
?? nested?.flows?.openai?.targets?.codex2api?.codex2apiUrl
?? nested?.flows?.openai?.integrationTargets?.codex2api?.codex2apiUrl
?? ''
).trim(),
codex2apiAdminKey: String(
input?.codex2apiAdminKey
?? nested?.flows?.openai?.targets?.codex2api?.codex2apiAdminKey
?? nested?.flows?.openai?.integrationTargets?.codex2api?.codex2apiAdminKey
?? ''
).trim(),
@@ -349,11 +383,14 @@
},
},
kiro: {
targetId: kiroTargetId,
selectedTargetId: kiroSelectedTargetId,
targets: {
'kiro-rs': {
...defaults.flows.kiro.targets['kiro-rs'],
...getIntegrationTargetValue(nested, (state) => state.flows?.kiro?.targets?.['kiro-rs']),
...getTargetValue(
nested,
(state) => state.flows?.kiro?.targets?.['kiro-rs']
),
baseUrl: String(
input?.kiroRsUrl
?? input?.kiroRsBaseUrl
@@ -419,20 +456,24 @@
const normalizedState = normalizeSettingsState(settingsState);
const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId);
const flowSettings = normalizedState?.flows?.[normalizedFlowId] || {};
if (normalizedFlowId === 'kiro') {
return normalizeTargetId(
normalizedFlowId,
flowSettings?.targetId,
defaultKiroTargetId
);
}
return normalizeTargetId(
normalizedFlowId,
flowSettings?.integrationTargetId,
defaultOpenAiTargetId
flowSettings?.selectedTargetId,
normalizedFlowId === 'kiro' ? defaultKiroTargetId : defaultOpenAiTargetId
);
}
function getTargetSettings(settingsState = {}, flowId, targetId = '') {
const normalizedState = normalizeSettingsState(settingsState);
const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId);
const resolvedTargetId = normalizeTargetId(
normalizedFlowId,
targetId || getSelectedTargetId(normalizedState, normalizedFlowId),
getSelectedTargetId(normalizedState, normalizedFlowId)
);
return cloneValue(normalizedState?.flows?.[normalizedFlowId]?.targets?.[resolvedTargetId] || {});
}
function buildStepExecutionRangeByFlow(settingsState = {}) {
const normalizedState = normalizeSettingsState(settingsState);
return {
@@ -455,21 +496,19 @@
const openaiState = normalizedState.flows.openai;
const kiroState = normalizedState.flows.kiro;
next.activeFlowId = normalizedState.activeFlowId;
next.openaiIntegrationTargetId = getSelectedTargetId(normalizedState, 'openai');
next.kiroTargetId = getSelectedTargetId(normalizedState, 'kiro');
next.panelMode = next.openaiIntegrationTargetId;
next.vpsUrl = openaiState.integrationTargets.cpa.vpsUrl;
next.vpsPassword = openaiState.integrationTargets.cpa.vpsPassword;
next.localCpaStep9Mode = openaiState.integrationTargets.cpa.localCpaStep9Mode;
next.sub2apiUrl = openaiState.integrationTargets.sub2api.sub2apiUrl;
next.sub2apiEmail = openaiState.integrationTargets.sub2api.sub2apiEmail;
next.sub2apiPassword = openaiState.integrationTargets.sub2api.sub2apiPassword;
next.sub2apiGroupName = openaiState.integrationTargets.sub2api.sub2apiGroupName;
next.sub2apiGroupNames = cloneValue(openaiState.integrationTargets.sub2api.sub2apiGroupNames);
next.sub2apiAccountPriority = openaiState.integrationTargets.sub2api.sub2apiAccountPriority;
next.sub2apiDefaultProxyName = openaiState.integrationTargets.sub2api.sub2apiDefaultProxyName;
next.codex2apiUrl = openaiState.integrationTargets.codex2api.codex2apiUrl;
next.codex2apiAdminKey = openaiState.integrationTargets.codex2api.codex2apiAdminKey;
next.targetId = getSelectedTargetId(normalizedState, normalizedState.activeFlowId);
next.vpsUrl = openaiState.targets.cpa.vpsUrl;
next.vpsPassword = openaiState.targets.cpa.vpsPassword;
next.localCpaStep9Mode = openaiState.targets.cpa.localCpaStep9Mode;
next.sub2apiUrl = openaiState.targets.sub2api.sub2apiUrl;
next.sub2apiEmail = openaiState.targets.sub2api.sub2apiEmail;
next.sub2apiPassword = openaiState.targets.sub2api.sub2apiPassword;
next.sub2apiGroupName = openaiState.targets.sub2api.sub2apiGroupName;
next.sub2apiGroupNames = cloneValue(openaiState.targets.sub2api.sub2apiGroupNames);
next.sub2apiAccountPriority = openaiState.targets.sub2api.sub2apiAccountPriority;
next.sub2apiDefaultProxyName = openaiState.targets.sub2api.sub2apiDefaultProxyName;
next.codex2apiUrl = openaiState.targets.codex2api.codex2apiUrl;
next.codex2apiAdminKey = openaiState.targets.codex2api.codex2apiAdminKey;
next.customPassword = normalizedState.services.account.customPassword;
next.signupMethod = openaiState.signup.signupMethod;
next.phoneVerificationEnabled = openaiState.signup.phoneVerificationEnabled;
@@ -497,11 +536,12 @@
const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId);
const targetId = getSelectedTargetId(normalizedState, normalizedFlowId);
if (normalizedFlowId === 'kiro') {
const targetSettings = getTargetSettings(normalizedState, normalizedFlowId, targetId);
return {
activeFlowId: normalizedFlowId,
targetId,
kiroRsUrl: normalizedState.flows.kiro.targets['kiro-rs'].baseUrl,
kiroRsKey: normalizedState.flows.kiro.targets['kiro-rs'].apiKey,
kiroRsUrl: targetSettings.baseUrl || '',
kiroRsKey: targetSettings.apiKey || '',
};
}
return {
@@ -517,6 +557,7 @@
getFlowInputState,
getFlowSettings,
getSelectedTargetId,
getTargetSettings,
mergeSettingsState,
normalizeSettingsState,
};
+486
View File
@@ -0,0 +1,486 @@
(function attachMultiPageSourceRegistry(root, factory) {
root.MultiPageSourceRegistry = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createSourceRegistryModule() {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
const flowRegistryApi = rootScope.MultiPageFlowRegistry || {};
const SHARED_SOURCE_DEFINITIONS = Object.freeze({
'qq-mail': {
flowId: null,
kind: 'mail-provider',
label: 'QQ \u90ae\u7bb1',
readyPolicy: 'top-frame-only',
family: 'qq-mail-family',
driverId: 'content/qq-mail',
cleanupScopes: [],
detectionMatchers: [
{ hostnames: ['mail.qq.com', 'wx.mail.qq.com'] },
],
familyMatchers: [
{ hostnames: ['mail.qq.com', 'wx.mail.qq.com'] },
],
},
'mail-163': {
flowId: null,
kind: 'mail-provider',
label: '163 \u90ae\u7bb1',
readyPolicy: 'top-frame-only',
family: 'mail-163-family',
driverId: 'content/mail-163',
cleanupScopes: [],
detectionMatchers: [
{
hostnames: ['mail.163.com', 'mail.126.com', 'webmail.vip.163.com'],
hostnameEndsWith: ['.mail.163.com', '.mail.126.com'],
matchMode: 'any',
},
],
familyMatchers: [
{
hostnames: ['mail.163.com', 'mail.126.com', 'webmail.vip.163.com'],
hostnameEndsWith: ['.mail.163.com', '.mail.126.com'],
matchMode: 'any',
},
],
},
'gmail-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Gmail \u90ae\u7bb1',
readyPolicy: 'top-frame-only',
family: 'gmail-mail-family',
driverId: 'content/gmail-mail',
cleanupScopes: [],
detectionMatchers: [
{ hostnames: ['mail.google.com'] },
],
familyMatchers: [
{ hostnames: ['mail.google.com'] },
],
},
'icloud-mail': {
flowId: null,
kind: 'mail-provider',
label: 'iCloud \u90ae\u7bb1',
readyPolicy: 'allow-child-frame',
family: 'icloud-mail-family',
driverId: 'content/icloud-mail',
cleanupScopes: [],
detectionMatchers: [
{ hostnames: ['www.icloud.com', 'www.icloud.com.cn'] },
],
familyMatchers: [
{ hostnames: ['www.icloud.com', 'www.icloud.com.cn'] },
],
},
'inbucket-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Inbucket \u90ae\u7bb1',
readyPolicy: 'top-frame-only',
family: 'inbucket-mail-family',
driverId: 'content/inbucket-mail',
cleanupScopes: [],
familyMatchers: [
{ originEqualsReference: true, pathPrefixes: ['/m/'] },
],
},
'mail-2925': {
flowId: null,
kind: 'mail-provider',
label: '2925 \u90ae\u7bb1',
readyPolicy: 'top-frame-only',
family: 'mail-2925-family',
driverId: 'content/mail-2925',
cleanupScopes: [],
detectionMatchers: [
{ hostnames: ['2925.com', 'www.2925.com'] },
],
familyMatchers: [
{ hostnames: ['2925.com', 'www.2925.com'] },
],
},
'duck-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Duck \u90ae\u7bb1',
readyPolicy: 'allow-child-frame',
family: 'duck-mail-family',
driverId: 'content/duck-mail',
cleanupScopes: [],
detectionMatchers: [
{ urlIncludes: 'duckduckgo.com/email/settings/autofill' },
],
familyMatchers: [
{ hostnames: ['duckduckgo.com'], pathPrefixes: ['/email/'] },
],
},
'unknown-source': {
flowId: null,
kind: 'unknown',
label: '\u672a\u77e5\u6765\u6e90',
readyPolicy: 'disabled',
family: 'unknown-family',
driverId: null,
cleanupScopes: [],
},
});
const SHARED_DRIVER_DEFINITIONS = Object.freeze({
'content/qq-mail': {
sourceId: 'qq-mail',
commands: ['POLL_EMAIL'],
},
'content/mail-163': {
sourceId: 'mail-163',
commands: ['POLL_EMAIL'],
},
'content/gmail-mail': {
sourceId: 'gmail-mail',
commands: ['POLL_EMAIL'],
},
'content/icloud-mail': {
sourceId: 'icloud-mail',
commands: ['POLL_EMAIL'],
},
'content/mail-2925': {
sourceId: 'mail-2925',
commands: ['POLL_EMAIL'],
},
'content/duck-mail': {
sourceId: 'duck-mail',
commands: ['FETCH_ALIAS_EMAIL'],
},
});
function normalizeHostname(hostname = '') {
return String(hostname || '').trim().toLowerCase();
}
function matchesNamedHostFamily(hostname = '', family = '') {
const normalizedHost = normalizeHostname(hostname);
const normalizedFamily = normalizeHostname(family);
if (!normalizedHost || !normalizedFamily) {
return false;
}
return normalizedHost === normalizedFamily
|| normalizedHost.endsWith(`.${normalizedFamily}`)
|| normalizedHost.startsWith(`${normalizedFamily}.`)
|| normalizedHost.includes(`.${normalizedFamily}.`);
}
function parseUrlSafely(rawUrl) {
if (!rawUrl) {
return null;
}
try {
return new URL(rawUrl);
} catch {
return null;
}
}
function buildCandidateUrl(url = '', hostname = '') {
const candidate = parseUrlSafely(url);
if (candidate) {
return candidate;
}
const normalizedHostname = normalizeHostname(hostname);
if (!normalizedHostname) {
return null;
}
return parseUrlSafely(`https://${normalizedHostname}/`);
}
function normalizeSourceId(source) {
return String(source || '').trim();
}
function normalizeStringArray(values = []) {
if (!Array.isArray(values)) {
return [];
}
return values
.map((value) => String(value || '').trim())
.filter(Boolean);
}
function hostnameMatchesSuffix(hostname = '', suffix = '') {
const normalizedHost = normalizeHostname(hostname);
const normalizedSuffix = normalizeHostname(suffix);
if (!normalizedHost || !normalizedSuffix) {
return false;
}
if (normalizedHost === normalizedSuffix) {
return true;
}
const dottedSuffix = normalizedSuffix.startsWith('.')
? normalizedSuffix
: `.${normalizedSuffix}`;
return normalizedHost.endsWith(dottedSuffix);
}
function getRuntimeSourceDefinitions() {
return {
...(typeof flowRegistryApi.getRuntimeSourceDefinitions === 'function'
? flowRegistryApi.getRuntimeSourceDefinitions()
: {}),
...SHARED_SOURCE_DEFINITIONS,
};
}
function getDriverDefinitions() {
return {
...(typeof flowRegistryApi.getDriverDefinitions === 'function'
? flowRegistryApi.getDriverDefinitions()
: {}),
...SHARED_DRIVER_DEFINITIONS,
};
}
function getSourceAliases() {
return {
...(typeof flowRegistryApi.getSourceAliases === 'function'
? flowRegistryApi.getSourceAliases()
: {}),
};
}
function createSourceRegistry() {
const SOURCE_DEFINITIONS = getRuntimeSourceDefinitions();
const DRIVER_DEFINITIONS = getDriverDefinitions();
const SOURCE_ALIASES = getSourceAliases();
function resolveCanonicalSource(source) {
const normalized = normalizeSourceId(source);
if (!normalized) {
return '';
}
return SOURCE_ALIASES[normalized] || normalized;
}
function getAliasKeysForCanonicalSource(source) {
const canonical = resolveCanonicalSource(source);
return Object.keys(SOURCE_ALIASES).filter((alias) => SOURCE_ALIASES[alias] === canonical);
}
function getSourceKeys(source) {
const normalized = normalizeSourceId(source);
const canonical = resolveCanonicalSource(normalized);
return Array.from(new Set([
canonical,
...getAliasKeysForCanonicalSource(canonical),
normalized,
].filter(Boolean)));
}
function getSourceMeta(source) {
const canonical = resolveCanonicalSource(source);
const definition = SOURCE_DEFINITIONS[canonical];
if (!definition) {
return null;
}
return {
id: canonical,
aliases: getAliasKeysForCanonicalSource(canonical),
...definition,
};
}
function getSourceLabel(source) {
return getSourceMeta(source)?.label || normalizeSourceId(source) || '\u672a\u77e5\u6765\u6e90';
}
function getDriverIdForSource(source) {
return getSourceMeta(source)?.driverId || null;
}
function getDriverMeta(sourceOrDriverId) {
const directDriverId = normalizeSourceId(sourceOrDriverId);
const driverId = Object.prototype.hasOwnProperty.call(DRIVER_DEFINITIONS, directDriverId)
? directDriverId
: getDriverIdForSource(sourceOrDriverId);
if (!driverId || !Object.prototype.hasOwnProperty.call(DRIVER_DEFINITIONS, driverId)) {
return null;
}
return {
id: driverId,
...DRIVER_DEFINITIONS[driverId],
};
}
function driverAcceptsCommand(sourceOrDriverId, command) {
const normalizedCommand = normalizeSourceId(command);
if (!normalizedCommand) {
return false;
}
const driver = getDriverMeta(sourceOrDriverId);
return Array.isArray(driver?.commands) && driver.commands.includes(normalizedCommand);
}
function matchesUrlRule(rule = {}, candidate, reference) {
if (!candidate) {
return false;
}
const matchMode = String(rule?.matchMode || 'all').trim().toLowerCase() === 'any'
? 'any'
: 'all';
const conditions = [];
const hostnames = normalizeStringArray(rule.hostnames);
if (hostnames.length) {
conditions.push(hostnames.includes(candidate.hostname));
}
const hostnameFamilies = normalizeStringArray(rule.hostnameFamilies);
if (hostnameFamilies.length) {
conditions.push(hostnameFamilies.some((family) => matchesNamedHostFamily(candidate.hostname, family)));
}
const hostnameEndsWith = normalizeStringArray(rule.hostnameEndsWith);
if (hostnameEndsWith.length) {
conditions.push(hostnameEndsWith.some((suffix) => hostnameMatchesSuffix(candidate.hostname, suffix)));
}
if (rule.hostnameRegex) {
conditions.push(new RegExp(rule.hostnameRegex, rule.hostnameRegexFlags || '').test(candidate.hostname));
}
if (rule.urlIncludes) {
conditions.push(String(candidate.href || '').includes(String(rule.urlIncludes)));
}
const pathPrefixes = normalizeStringArray(rule.pathPrefixes);
if (pathPrefixes.length) {
conditions.push(pathPrefixes.some((prefix) => candidate.pathname.startsWith(prefix)));
}
if (rule.pathEquals) {
conditions.push(candidate.pathname === String(rule.pathEquals));
}
const pathEqualsOneOf = normalizeStringArray(rule.pathEqualsOneOf);
if (pathEqualsOneOf.length) {
conditions.push(pathEqualsOneOf.includes(candidate.pathname));
}
if (rule.originEqualsReference) {
conditions.push(Boolean(reference) && candidate.origin === reference.origin);
}
if (rule.pathEqualsReference) {
conditions.push(Boolean(reference) && candidate.pathname === reference.pathname);
}
if (!conditions.length) {
return false;
}
return matchMode === 'any'
? conditions.some(Boolean)
: conditions.every(Boolean);
}
function matchesSourceDetection(source, url = '', hostname = '') {
const candidate = buildCandidateUrl(url, hostname);
if (!candidate) {
return false;
}
const canonical = resolveCanonicalSource(source);
const detectionMatchers = getSourceMeta(canonical)?.detectionMatchers || [];
return detectionMatchers.some((rule) => matchesUrlRule(rule, candidate, null));
}
function isSignupPageHost(hostname = '') {
return matchesSourceDetection('openai-auth', '', hostname);
}
function isSignupEntryHost(hostname = '') {
return matchesSourceDetection('chatgpt', '', hostname);
}
function is163MailHost(hostname = '') {
return matchesSourceDetection('mail-163', '', hostname);
}
function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
const candidate = parseUrlSafely(candidateUrl);
if (!candidate) {
return false;
}
const canonical = resolveCanonicalSource(source);
const familyMatchers = getSourceMeta(canonical)?.familyMatchers || [];
const reference = parseUrlSafely(referenceUrl);
return familyMatchers.some((rule) => matchesUrlRule(rule, candidate, reference));
}
function detectSourceFromLocation({
injectedSource,
url = '',
hostname = '',
} = {}) {
if (injectedSource) {
return resolveCanonicalSource(injectedSource);
}
const candidate = buildCandidateUrl(url, hostname);
if (!candidate) {
return 'unknown-source';
}
const detectionSourceIds = Object.keys(SOURCE_DEFINITIONS).filter((sourceId) => sourceId !== 'unknown-source');
for (const sourceId of detectionSourceIds) {
if (matchesSourceDetection(sourceId, candidate.href, candidate.hostname)) {
return sourceId;
}
}
return 'unknown-source';
}
function shouldReportReadyForFrame(source, isChildFrame) {
const canonical = resolveCanonicalSource(source);
const readyPolicy = getSourceMeta(canonical)?.readyPolicy || 'allow-child-frame';
if (readyPolicy === 'disabled') {
return false;
}
if (!isChildFrame) {
return true;
}
return readyPolicy === 'allow-child-frame';
}
function getCleanupOwnerSource(cleanupScope) {
const normalizedCleanupScope = String(cleanupScope || '').trim();
const owner = Object.keys(SOURCE_DEFINITIONS).find((sourceId) => {
const cleanupScopes = Array.isArray(SOURCE_DEFINITIONS[sourceId]?.cleanupScopes)
? SOURCE_DEFINITIONS[sourceId].cleanupScopes
: [];
return cleanupScopes.includes(normalizedCleanupScope);
});
return resolveCanonicalSource(owner || '');
}
return {
detectSourceFromLocation,
driverAcceptsCommand,
getCleanupOwnerSource,
getDriverIdForSource,
getDriverMeta,
getSourceKeys,
getSourceLabel,
getSourceMeta,
is163MailHost,
isSignupEntryHost,
isSignupPageHost,
matchesSourceUrlFamily,
parseUrlSafely,
resolveCanonicalSource,
shouldReportReadyForFrame,
};
}
return {
createSourceRegistry,
};
});
@@ -76,7 +76,7 @@
if (sourceRegistry?.getCleanupOwnerSource) {
return sourceRegistry.getCleanupOwnerSource(cleanupScope);
}
return cleanupScope === 'oauth-localhost-callback' ? 'signup-page' : '';
return cleanupScope === 'oauth-localhost-callback' ? 'openai-auth' : '';
}
function normalizeAutomationWindowId(value) {
+73 -585
View File
@@ -1,589 +1,80 @@
(function attachStepDefinitions(root, factory) {
root.MultiPageStepDefinitions = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createStepDefinitionsModule() {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
const flowsIndexApi = rootScope.MultiPageFlowsIndex || {};
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_PAYPAL_HOSTED = 'paypal-hosted';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION = 'cpa_codex_session';
const PLUS_PAYMENT_STEP_KEY = 'paypal-approve';
const SIGNUP_METHOD_EMAIL = 'email';
const SIGNUP_METHOD_PHONE = 'phone';
const NORMAL_PREFIX_STEP_DEFINITIONS = [
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网', sourceId: 'chatgpt', driverId: null, command: 'open-chatgpt' },
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-signup-email' },
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-password' },
{ id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-signup-code' },
{ id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-profile' },
{ id: 6, order: 60, key: 'wait-registration-success', title: '等待注册成功', sourceId: 'chatgpt', driverId: null, command: 'wait-registration-success' },
];
const PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS = [
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网', sourceId: 'chatgpt', driverId: null, command: 'open-chatgpt' },
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-signup-email' },
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-password' },
{ id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-signup-code' },
{ id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-profile' },
{ id: 6, order: 60, key: 'plus-checkout-create', title: '创建 Plus Checkout', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-create' },
{ id: 7, order: 70, key: 'plus-checkout-billing', title: '填写账单并提交订单', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-billing' },
{ id: 8, order: 80, key: 'paypal-approve', title: 'PayPal 登录与授权', sourceId: 'paypal-flow', driverId: 'content/paypal-flow', command: 'paypal-approve' },
{ id: 9, order: 90, key: 'plus-checkout-return', title: '订阅回跳确认', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-return' },
];
const PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS = [
...PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS.slice(0, 6),
{ id: 7, order: 70, key: 'paypal-hosted-email', title: '无卡直绑填写 PayPal 邮箱', sourceId: 'paypal-flow', driverId: 'content/paypal-flow', command: 'paypal-hosted-email' },
{ id: 8, order: 80, key: 'paypal-hosted-card', title: '无卡直绑填写 PayPal 资料', sourceId: 'paypal-flow', driverId: 'content/paypal-flow', command: 'paypal-hosted-card' },
{ id: 9, order: 90, key: 'paypal-hosted-create-account', title: '无卡直绑确认创建 PayPal', sourceId: 'paypal-flow', driverId: 'content/paypal-flow', command: 'paypal-hosted-create-account' },
{ id: 10, order: 100, key: 'paypal-hosted-review', title: '无卡直绑完成 PayPal 授权', sourceId: 'paypal-flow', driverId: 'content/paypal-flow', command: 'paypal-hosted-review' },
];
const PLUS_GOPAY_PREFIX_STEP_DEFINITIONS = [
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网', sourceId: 'chatgpt', driverId: null, command: 'open-chatgpt' },
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-signup-email' },
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-password' },
{ id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-signup-code' },
{ id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-profile' },
{ id: 6, order: 60, key: 'plus-checkout-create', title: '打开 GoPay 订阅页', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-create' },
{ id: 7, order: 70, key: 'gopay-subscription-confirm', title: '等待 GoPay 订阅确认', sourceId: 'gopay-flow', driverId: 'content/gopay-flow', command: 'gopay-subscription-confirm' },
];
const PLUS_GPC_PREFIX_STEP_DEFINITIONS = [
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网', sourceId: 'chatgpt', driverId: null, command: 'open-chatgpt' },
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-signup-email' },
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-password' },
{ id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-signup-code' },
{ id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fill-profile' },
{ id: 6, order: 60, key: 'plus-checkout-create', title: '创建 GPC 订单', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-create' },
{ id: 7, order: 70, key: 'plus-checkout-billing', title: '等待 GPC 任务完成', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-billing' },
];
function isPhoneSignupReloginAfterBindEmailEnabled(options = {}) {
return Boolean(options?.phoneSignupReloginAfterBindEmailEnabled);
}
function createOpenAiAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
const id = Number(startId) || 7;
const order = Number(startOrder) || id * 10;
const commonStart = [
{ id, order, key: 'oauth-login', title: '刷新 OAuth 并登录', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
{ id: id + 1, order: order + 10, key: 'fetch-login-code', title: '获取登录验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
];
if (signupMethod === SIGNUP_METHOD_PHONE) {
if (isPhoneSignupReloginAfterBindEmailEnabled(options)) {
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
{ id: id + 3, order: order + 30, key: 'fetch-bind-email-code', title: '获取绑定邮箱验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fetch-bind-email-code', mailRuleId: 'openai-login-code' },
{ id: id + 4, order: order + 40, key: 'relogin-bound-email', title: '绑定邮箱后刷新 OAuth 并登录(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
{ id: id + 5, order: order + 50, key: 'fetch-bound-email-login-code', title: '获取登录验证码(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
{ id: id + 6, order: order + 60, key: 'post-bound-email-phone-verification', title: '手机号验证', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'post-login-phone-verification' },
{ id: id + 7, order: order + 70, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 8, order: order + 80, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
}
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
{ id: id + 3, order: order + 30, key: 'fetch-bind-email-code', title: '获取绑定邮箱验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fetch-bind-email-code', mailRuleId: 'openai-login-code' },
{ id: id + 4, order: order + 40, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 5, order: order + 50, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
function normalizeRegisteredFlowIds(values = []) {
if (!Array.isArray(values)) {
return [];
}
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'post-login-phone-verification', title: '手机号验证', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'post-login-phone-verification' },
{ id: id + 3, order: order + 30, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 4, order: order + 40, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
const seen = new Set();
return values
.map((value) => String(value || '').trim().toLowerCase())
.filter((value) => {
if (!value || seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
function createSub2ApiSessionImportTail(startId, startOrder) {
const id = Number(startId) || 10;
const order = Number(startOrder) || id * 10;
return [
{
id,
order,
key: 'sub2api-session-import',
title: '导入当前 ChatGPT 会话到 SUB2API',
sourceId: 'sub2api-panel',
driverId: 'background/sub2api-session-import',
command: 'sub2api-session-import',
},
];
function getRegisteredFlowIds() {
return normalizeRegisteredFlowIds(
typeof flowsIndexApi.getRegisteredFlowIds === 'function'
? flowsIndexApi.getRegisteredFlowIds()
: [DEFAULT_ACTIVE_FLOW_ID]
).filter((flowId) => Boolean(getFlowWorkflow(flowId)));
}
function createCpaSessionImportTail(startId, startOrder) {
const id = Number(startId) || 10;
const order = Number(startOrder) || id * 10;
return [
{
id,
order,
key: 'cpa-session-import',
title: '导入当前 ChatGPT 会话到 CPA',
sourceId: 'vps-panel',
driverId: 'background/cpa-session-import',
command: 'cpa-session-import',
},
];
function getFlowWorkflow(flowId) {
return typeof flowsIndexApi.getFlowWorkflow === 'function'
? flowsIndexApi.getFlowWorkflow(flowId)
: null;
}
function normalizePlusAccountAccessStrategy(value = '') {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION;
}
if (normalized === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION;
}
return PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
}
function resolvePlusSessionImportTail(options = {}, signupMethod = SIGNUP_METHOD_EMAIL) {
if (signupMethod !== SIGNUP_METHOD_EMAIL) {
return null;
}
const strategy = normalizePlusAccountAccessStrategy(options?.plusAccountAccessStrategy);
if (strategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return createSub2ApiSessionImportTail;
}
if (strategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return createCpaSessionImportTail;
}
return null;
}
function createOpenAiSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
const sessionTailFactory = resolvePlusSessionImportTail(options, signupMethod);
const tailSteps = sessionTailFactory
? sessionTailFactory(startId, startOrder)
: createOpenAiAuthTail(startId, startOrder, signupMethod, options);
return [
...prefixSteps,
...tailSteps,
];
}
function createHostedCheckoutAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
const id = Number(startId) || 7;
const order = Number(startOrder) || id * 10;
const commonStart = [
{ id, order, key: 'oauth-login', title: '刷新 OAuth 并登录', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
{ id: id + 1, order: order + 10, key: 'fetch-login-code', title: '获取登录验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
];
if (signupMethod === SIGNUP_METHOD_PHONE) {
if (isPhoneSignupReloginAfterBindEmailEnabled(options)) {
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
{ id: id + 3, order: order + 30, key: 'fetch-bind-email-code', title: '获取绑定邮箱验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fetch-bind-email-code', mailRuleId: 'openai-login-code' },
{ id: id + 4, order: order + 40, key: 'relogin-bound-email', title: '绑定邮箱后刷新 OAuth 并登录(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
{ id: id + 5, order: order + 50, key: 'fetch-bound-email-login-code', title: '获取登录验证码(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
{ id: id + 6, order: order + 60, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 7, order: order + 70, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
}
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
{ id: id + 3, order: order + 30, key: 'fetch-bind-email-code', title: '获取绑定邮箱验证码', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'fetch-bind-email-code', mailRuleId: 'openai-login-code' },
{ id: id + 4, order: order + 40, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 5, order: order + 50, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
}
return [
...commonStart,
{ id: id + 2, order: order + 20, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
{ id: id + 3, order: order + 30, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
];
}
function createHostedCheckoutSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
const sessionTailFactory = resolvePlusSessionImportTail(options, signupMethod);
const tailSteps = sessionTailFactory
? sessionTailFactory(startId, startOrder)
: createHostedCheckoutAuthTail(startId, startOrder, signupMethod, options);
return [
...prefixSteps,
...tailSteps,
];
}
const NORMAL_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_EMAIL);
const NORMAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE);
const NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_PAYPAL_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_PAYPAL_SUB2API_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION }
);
const PLUS_PAYPAL_CPA_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION }
);
const PLUS_PAYPAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_PAYPAL_HOSTED_CHECKOUT_STEP_DEFINITIONS = createHostedCheckoutSteps(PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS, 11, 110, SIGNUP_METHOD_EMAIL);
const PLUS_PAYPAL_HOSTED_CHECKOUT_SUB2API_SESSION_STEP_DEFINITIONS = createHostedCheckoutSteps(
PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS,
11,
110,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION }
);
const PLUS_PAYPAL_HOSTED_CHECKOUT_CPA_SESSION_STEP_DEFINITIONS = createHostedCheckoutSteps(
PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS,
11,
110,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION }
);
const PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_STEP_DEFINITIONS = createHostedCheckoutSteps(PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS, 11, 110, SIGNUP_METHOD_PHONE);
const PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createHostedCheckoutSteps(PLUS_PAYPAL_HOSTED_CHECKOUT_PREFIX_STEP_DEFINITIONS, 11, 110, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_GOPAY_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_GOPAY_SUB2API_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_GOPAY_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION }
);
const PLUS_GOPAY_CPA_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_GOPAY_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION }
);
const PLUS_GOPAY_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const PLUS_GPC_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
const PLUS_GPC_SUB2API_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_GPC_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION }
);
const PLUS_GPC_CPA_SESSION_STEP_DEFINITIONS = createOpenAiSteps(
PLUS_GPC_PREFIX_STEP_DEFINITIONS,
10,
100,
SIGNUP_METHOD_EMAIL,
{ plusAccountAccessStrategy: PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION }
);
const PLUS_GPC_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
const PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
const KIRO_STEP_DEFINITIONS = [
{
id: 1,
order: 10,
key: 'kiro-open-register-page',
title: '打开注册页',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-open-register-page',
},
{
id: 2,
order: 20,
key: 'kiro-submit-email',
title: '获取邮箱并继续',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-submit-email',
},
{
id: 3,
order: 30,
key: 'kiro-submit-name',
title: '填写姓名并继续',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-submit-name',
},
{
id: 4,
order: 40,
key: 'kiro-submit-verification-code',
title: '获取验证码并继续',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-submit-verification-code',
},
{
id: 5,
order: 50,
key: 'kiro-submit-password',
title: '设置密码并继续',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-submit-password',
},
{
id: 6,
order: 60,
key: 'kiro-complete-register-consent',
title: '完成注册授权',
sourceId: 'kiro-register-page',
driverId: 'background/kiro-register',
command: 'kiro-complete-register-consent',
},
{
id: 7,
order: 70,
key: 'kiro-start-desktop-authorize',
title: '启动桌面授权',
sourceId: 'kiro-desktop-authorize',
driverId: 'background/kiro-desktop-authorize',
command: 'kiro-start-desktop-authorize',
},
{
id: 8,
order: 80,
key: 'kiro-complete-desktop-authorize',
title: '完成桌面授权',
sourceId: 'kiro-desktop-authorize',
driverId: 'background/kiro-desktop-authorize',
command: 'kiro-complete-desktop-authorize',
},
{
id: 9,
order: 90,
key: 'kiro-upload-credential',
title: '上传凭据到 kiro.rs',
sourceId: 'kiro-rs-admin',
driverId: 'background/kiro-publisher-kiro-rs',
command: 'kiro-upload-credential',
},
];
const PHONE_SIGNUP_TITLE_OVERRIDES = Object.freeze({
'submit-signup-email': '注册并输入手机号',
'fetch-signup-code': '获取手机验证码',
});
const KIRO_CONTRIBUTION_STEP_TITLE = '贡献上传';
function isPlusModeEnabled(options = {}) {
return Boolean(options?.plusModeEnabled || options?.plusMode);
}
function normalizePlusPaymentMethod(value = '') {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === PLUS_PAYMENT_METHOD_PAYPAL_HOSTED || normalized === 'paypal_direct' || normalized === 'paypal-direct') {
return PLUS_PAYMENT_METHOD_PAYPAL_HOSTED;
}
if (normalized === PLUS_PAYMENT_METHOD_GPC_HELPER) {
return PLUS_PAYMENT_METHOD_GPC_HELPER;
}
return normalized === PLUS_PAYMENT_METHOD_GOPAY ? PLUS_PAYMENT_METHOD_GOPAY : PLUS_PAYMENT_METHOD_PAYPAL;
}
function normalizeSignupMethod(value = '') {
return String(value || '').trim().toLowerCase() === SIGNUP_METHOD_PHONE
? SIGNUP_METHOD_PHONE
: SIGNUP_METHOD_EMAIL;
function hasFlow(flowId) {
return Boolean(getFlowWorkflow(flowId));
}
function normalizeActiveFlowId(value = '', fallback = DEFAULT_ACTIVE_FLOW_ID) {
const normalized = String(value || '').trim().toLowerCase();
if (normalized) {
if (hasFlow(normalized)) {
return normalized;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
return fallbackValue || DEFAULT_ACTIVE_FLOW_ID;
if (hasFlow(fallbackValue)) {
return fallbackValue;
}
return getRegisteredFlowIds()[0] || DEFAULT_ACTIVE_FLOW_ID;
}
function getResolvedSignupMethod(options = {}) {
return normalizeSignupMethod(options?.resolvedSignupMethod || options?.signupMethod);
}
function getOpenAiModeStepDefinitions(options = {}) {
const signupMethod = getResolvedSignupMethod(options);
const reloginAfterBindEmail = signupMethod === SIGNUP_METHOD_PHONE
&& isPhoneSignupReloginAfterBindEmailEnabled(options);
if (!isPlusModeEnabled(options)) {
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: NORMAL_PHONE_STEP_DEFINITIONS;
}
return NORMAL_STEP_DEFINITIONS;
}
const paymentMethod = normalizePlusPaymentMethod(options?.plusPaymentMethod || options?.paymentMethod);
const plusAccountAccessStrategy = normalizePlusAccountAccessStrategy(options?.plusAccountAccessStrategy);
if (paymentMethod === PLUS_PAYMENT_METHOD_PAYPAL_HOSTED) {
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return PLUS_PAYPAL_HOSTED_CHECKOUT_SUB2API_SESSION_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return PLUS_PAYPAL_HOSTED_CHECKOUT_CPA_SESSION_STEP_DEFINITIONS;
}
return PLUS_PAYPAL_HOSTED_CHECKOUT_STEP_DEFINITIONS;
}
if (paymentMethod === PLUS_PAYMENT_METHOD_GPC_HELPER) {
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_GPC_PHONE_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return PLUS_GPC_SUB2API_SESSION_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return PLUS_GPC_CPA_SESSION_STEP_DEFINITIONS;
}
return PLUS_GPC_STEP_DEFINITIONS;
}
if (paymentMethod === PLUS_PAYMENT_METHOD_GOPAY) {
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_GOPAY_PHONE_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return PLUS_GOPAY_SUB2API_SESSION_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return PLUS_GOPAY_CPA_SESSION_STEP_DEFINITIONS;
}
return PLUS_GOPAY_STEP_DEFINITIONS;
}
if (signupMethod === SIGNUP_METHOD_PHONE) {
return reloginAfterBindEmail
? PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
: PLUS_PAYPAL_PHONE_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION) {
return PLUS_PAYPAL_SUB2API_SESSION_STEP_DEFINITIONS;
}
if (plusAccountAccessStrategy === PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION) {
return PLUS_PAYPAL_CPA_SESSION_STEP_DEFINITIONS;
}
return PLUS_PAYPAL_STEP_DEFINITIONS;
}
function getOpenAiPlusPaymentStepTitle(options = {}) {
if (!isPlusModeEnabled(options)) {
return '';
}
const paymentStep = getOpenAiModeStepDefinitions({
...options,
plusModeEnabled: true,
}).find((step) => step.key === PLUS_PAYMENT_STEP_KEY);
return paymentStep?.title || '';
}
function getOpenAiResolvedStepTitle(step = {}, options = {}) {
if (isPlusModeEnabled(options) && step.key === PLUS_PAYMENT_STEP_KEY) {
return getOpenAiPlusPaymentStepTitle(options) || step.title;
}
const signupMethod = getResolvedSignupMethod(options);
if (signupMethod === SIGNUP_METHOD_PHONE && PHONE_SIGNUP_TITLE_OVERRIDES[step.key]) {
return PHONE_SIGNUP_TITLE_OVERRIDES[step.key];
}
return step.title;
}
function isKiroContributionModeEnabled(options = {}) {
return Boolean(options?.accountContributionEnabled || options?.state?.accountContributionEnabled);
}
const FLOW_DEFINITION_BUILDERS = Object.freeze({
openai: {
getAllSteps() {
const keyed = new Map();
for (const step of [
...NORMAL_STEP_DEFINITIONS,
...NORMAL_PHONE_STEP_DEFINITIONS,
...NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_PAYPAL_STEP_DEFINITIONS,
...PLUS_PAYPAL_SUB2API_SESSION_STEP_DEFINITIONS,
...PLUS_PAYPAL_CPA_SESSION_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
...PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_PAYPAL_HOSTED_CHECKOUT_STEP_DEFINITIONS,
...PLUS_PAYPAL_HOSTED_CHECKOUT_SUB2API_SESSION_STEP_DEFINITIONS,
...PLUS_PAYPAL_HOSTED_CHECKOUT_CPA_SESSION_STEP_DEFINITIONS,
...PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_STEP_DEFINITIONS,
...PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GOPAY_STEP_DEFINITIONS,
...PLUS_GOPAY_SUB2API_SESSION_STEP_DEFINITIONS,
...PLUS_GOPAY_CPA_SESSION_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
...PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
...PLUS_GPC_STEP_DEFINITIONS,
...PLUS_GPC_SUB2API_SESSION_STEP_DEFINITIONS,
...PLUS_GPC_CPA_SESSION_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_STEP_DEFINITIONS,
...PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
]) {
keyed.set(`${step.id}:${step.key}`, step);
}
return Array.from(keyed.values()).sort((left, right) => {
const leftOrder = Number.isFinite(left.order) ? left.order : left.id;
const rightOrder = Number.isFinite(right.order) ? right.order : right.id;
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
return left.id - right.id;
});
},
getModeStepDefinitions: getOpenAiModeStepDefinitions,
getPlusPaymentStepTitle: getOpenAiPlusPaymentStepTitle,
resolveStepTitle: getOpenAiResolvedStepTitle,
},
kiro: {
getAllSteps() {
return KIRO_STEP_DEFINITIONS;
},
getModeStepDefinitions() {
return KIRO_STEP_DEFINITIONS;
},
getPlusPaymentStepTitle() {
return '';
},
resolveStepTitle(step, options = {}) {
if (step?.key === 'kiro-upload-credential' && isKiroContributionModeEnabled(options)) {
return KIRO_CONTRIBUTION_STEP_TITLE;
}
return step?.title || '';
},
},
});
function hasFlow(flowId) {
const normalizedFlowId = normalizeActiveFlowId(flowId, '');
return Boolean(normalizedFlowId && FLOW_DEFINITION_BUILDERS[normalizedFlowId]);
}
function getRegisteredFlowIds() {
return Object.keys(FLOW_DEFINITION_BUILDERS);
function hasExplicitActiveFlowId(options = {}) {
return Boolean(
options
&& typeof options === 'object'
&& Object.prototype.hasOwnProperty.call(options, 'activeFlowId')
&& String(options.activeFlowId || '').trim()
);
}
function getFlowDefinitionBuilder(options = {}) {
const flowId = normalizeActiveFlowId(options?.activeFlowId, DEFAULT_ACTIVE_FLOW_ID);
const rawFlowId = String(options?.activeFlowId || '').trim().toLowerCase();
if (hasExplicitActiveFlowId(options) && rawFlowId && !hasFlow(rawFlowId)) {
return {
flowId: rawFlowId,
builder: null,
};
}
const flowId = normalizeActiveFlowId(rawFlowId, DEFAULT_ACTIVE_FLOW_ID);
return {
flowId,
builder: FLOW_DEFINITION_BUILDERS[flowId] || null,
builder: getFlowWorkflow(flowId),
};
}
@@ -656,7 +147,9 @@
}
return cloneNodes(builder.getAllSteps(options), options, flowId)
.sort((left, right) => {
if (left.displayOrder !== right.displayOrder) return left.displayOrder - right.displayOrder;
if (left.displayOrder !== right.displayOrder) {
return left.displayOrder - right.displayOrder;
}
return left.nodeId.localeCompare(right.nodeId);
});
}
@@ -712,7 +205,7 @@
}
function getWorkflow(options = {}) {
const flowId = normalizeActiveFlowId(options?.activeFlowId, DEFAULT_ACTIVE_FLOW_ID);
const { flowId } = getFlowDefinitionBuilder(options);
const nodes = getNodes(options);
return {
flowId,
@@ -722,34 +215,32 @@
};
}
const defaultWorkflowBuilder = getFlowWorkflow(DEFAULT_ACTIVE_FLOW_ID);
const STEP_DEFINITIONS = cloneSteps(
defaultWorkflowBuilder?.getVariantStepDefinitions
? defaultWorkflowBuilder.getVariantStepDefinitions('normal')
: (defaultWorkflowBuilder?.getModeStepDefinitions?.({ activeFlowId: DEFAULT_ACTIVE_FLOW_ID }) || []),
{},
DEFAULT_ACTIVE_FLOW_ID
);
const NORMAL_STEP_DEFINITIONS = STEP_DEFINITIONS;
const PLUS_STEP_DEFINITIONS = cloneSteps(
defaultWorkflowBuilder?.getVariantStepDefinitions
? defaultWorkflowBuilder.getVariantStepDefinitions('plusPaypal')
: [],
{ plusModeEnabled: true },
DEFAULT_ACTIVE_FLOW_ID
);
return {
DEFAULT_ACTIVE_FLOW_ID,
STEP_DEFINITIONS: NORMAL_STEP_DEFINITIONS,
NORMAL_STEP_DEFINITIONS,
NORMAL_PHONE_STEP_DEFINITIONS,
NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_STEP_DEFINITIONS: PLUS_PAYPAL_STEP_DEFINITIONS,
PLUS_PAYPAL_STEP_DEFINITIONS,
PLUS_PAYPAL_SUB2API_SESSION_STEP_DEFINITIONS,
PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_PAYPAL_HOSTED_CHECKOUT_STEP_DEFINITIONS,
PLUS_PAYPAL_HOSTED_CHECKOUT_SUB2API_SESSION_STEP_DEFINITIONS,
PLUS_PAYPAL_HOSTED_CHECKOUT_CPA_SESSION_STEP_DEFINITIONS,
PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_STEP_DEFINITIONS,
PLUS_PAYPAL_HOSTED_CHECKOUT_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_GOPAY_STEP_DEFINITIONS,
PLUS_GOPAY_SUB2API_SESSION_STEP_DEFINITIONS,
PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_GPC_STEP_DEFINITIONS,
PLUS_GPC_SUB2API_SESSION_STEP_DEFINITIONS,
PLUS_GPC_PHONE_STEP_DEFINITIONS,
PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
PLUS_STEP_DEFINITIONS,
SIGNUP_METHOD_EMAIL,
SIGNUP_METHOD_PHONE,
getAllSteps,
STEP_DEFINITIONS,
getAllNodes,
getAllSteps,
getLastStepId,
getNodeByDisplayOrder,
getNodeById,
@@ -762,9 +253,6 @@
getSteps,
getWorkflow,
hasFlow,
isPlusModeEnabled,
normalizeActiveFlowId,
normalizePlusPaymentMethod,
normalizeSignupMethod,
};
});
@@ -1,4 +1,4 @@
# 步骤与Flow节点化重构开发方案
# 步骤与Flow节点化重构开发方案
## 1. 文档目标
@@ -19,17 +19,17 @@
| --- | --- | --- |
| 状态 | `background.js` 里仍有 `currentStep``stepStatuses` | 数字步骤同时承担状态、顺序和恢复锚点 |
| 协议 | `EXECUTE_STEP``STEP_COMPLETE``STEP_ERROR``SKIP_STEP` | 消息协议仍以数字 step 为核心 |
| 定义 | `data/step-definitions.js``background/steps/registry.js` | 只有 OpenAI 真正注册,且执行仍按数字 id |
| 定义 | `data/step-definitions.js``core/flow-kernel/step-registry.js` | 只有 OpenAI 真正注册,且执行仍按数字 id |
| UI | `sidepanel/sidepanel.js` 按 step 渲染、发送、跳过 | 侧边栏本质上还是数字步骤面板 |
| 内容脚本 | `content/signup-page.js``message.step` 分支 | 页面驱动逻辑被 step number 绑死 |
| 运行态 | `background/runtime-state.js` 已有 `currentNodeId``nodeStatuses` | 但仍通过 legacy step 视图回写,双模型并存 |
| 外围能力 | `shared/source-registry.js``shared/flow-capabilities.js``background/mail-rule-registry.js` | 已有抽象,但都还没成为唯一事实来源 |
| 内容脚本 | `flows/openai/content/openai-auth.js``message.step` 分支 | 页面驱动逻辑被 step number 绑死 |
| 运行态 | `core/flow-kernel/runtime-state.js` 已有 `currentNodeId``nodeStatuses` | 但仍通过 legacy step 视图回写,双模型并存 |
| 外围能力 | `core/flow-kernel/source-registry.js``core/flow-kernel/flow-capabilities.js``background/mail-rule-registry.js` | 已有抽象,但都还没成为唯一事实来源 |
重点文件里最能说明问题的几个点:
- `background.js``DEFAULT_STATE``setStepStatus()``skipStep()``runAutoSequenceFromStep()``getStepRegistryForState()` 仍把数字 step 当主流程。
- `background/message-router.js``STEP_COMPLETE` / `STEP_ERROR` / `EXECUTE_STEP` / `SKIP_STEP` 还在消息层面锁死 step。
- `background/runtime-state.js`:已经有 node 相关字段,但还在做 step 兼容派生。
- `core/flow-kernel/runtime-state.js`:已经有 node 相关字段,但还在做 step 兼容派生。
- `sidepanel/sidepanel.js`:步骤列表、状态展示、手动执行、跳过、自动运行都还围着 step。
- `flows/openai/mail-rules.js``background/mail-rule-registry.js`:已经说明“规则可以 flow 化”,但目前只有 OpenAI 在用。
@@ -164,7 +164,7 @@ node = {
type: 'task',
order: 20, // 仅用于展示,不参与身份
sourceId: 'openai-auth',
driverId: 'content/signup-page',
driverId: 'flows/openai/content/openai-auth',
next: ['fill-password'],
retryPolicy: { maxAttempts: 3 },
recoveryPolicy: { onFailure: 'restart-node' },
@@ -241,11 +241,11 @@ flows/<flowId>/
### 6.3 content scripts
`content/signup-page.js` 这类脚本要从“按 step 分支”改成“按 node action / command 分支”。
`flows/openai/content/openai-auth.js` 这类脚本要从“按 step 分支”改成“按 node action / command 分支”。
### 6.4 source / driver
`shared/source-registry.js` 只负责页面来源、标签页生命周期和注入范围。
`core/flow-kernel/source-registry.js` 只负责页面来源、标签页生命周期和注入范围。
`driverRegistry` 只负责“这个 source 能接什么命令”。
@@ -378,3 +378,4 @@ flows/<flowId>/
- 邮件规则是否只从当前 flow definition 派生。
- sidepanel 是否能仅通过 workflow nodes 渲染,不新增全局步骤 switch。
- 自动运行、历史和日志是否都能用 `flowId/runId/nodeId` 定位。
File diff suppressed because it is too large Load Diff
@@ -94,9 +94,9 @@
文件:
1. `background/kiro/state.js`
2. `background/kiro/desktop-authorize-runner.js`
3. `background/kiro/publisher-kiro-rs.js`
1. `flows/kiro/background/state.js`
2. `flows/kiro/background/desktop-authorize-runner.js`
3. `flows/kiro/background/publisher-kiro-rs.js`
4. `data/step-definitions.js`
当前事实:
@@ -434,7 +434,7 @@ const FLOW_CONTRIBUTION_ADAPTERS = {
};
```
### 4.2 修改 `shared/flow-registry.js`
### 4.2 修改 `core/flow-kernel/flow-registry.js`
新增 capability
@@ -449,7 +449,7 @@ supportsAccountContribution: true
3. `supportsContributionMode` 仅作为迁移字段映射到 `supportsAccountContribution`
4. 新增 flow 只有在注册 adapter 后才能设置 `supportsAccountContribution: true`
### 4.3 修改 `shared/flow-capabilities.js`
### 4.3 修改 `core/flow-kernel/flow-capabilities.js`
处理规则:
@@ -499,7 +499,7 @@ flowContributionRuntime: {
新增文件:
```text
background/kiro/credential-artifact.js
flows/kiro/background/credential-artifact.js
```
职责:
@@ -515,8 +515,8 @@ background/kiro/credential-artifact.js
修改位置:
1. `background/kiro/desktop-authorize-runner.js`
2. `background/kiro/publisher-kiro-rs.js`
1. `flows/kiro/background/desktop-authorize-runner.js`
2. `flows/kiro/background/publisher-kiro-rs.js`
3. Kiro runner 调度处
处理规则:
@@ -782,7 +782,7 @@ POLL_FLOW_CONTRIBUTION_STATUS
开发内容:
1. 新增 `background/kiro/credential-artifact.js`
1. 新增 `flows/kiro/background/credential-artifact.js`
2. 新增 `background/contribution/adapters/kiro-builder-id.js`
3. 复用 Kiro runtime 产物构建 artifact。
4. 增加本地校验。
@@ -1,4 +1,4 @@
# 多注册流程来源与驱动注册设计
# 多注册流程来源与驱动注册设计
本文解决的是另一个会在第二个 flow 接入时立刻爆炸的问题:当前项目虽然有 `tab-runtime`,但“来源 source 是谁、该注入什么脚本、URL family 怎么判定、localhost callback 应该清谁”仍然带着明显的 OpenAI 单流程假设。
@@ -10,7 +10,7 @@
- `content/utils.js``detectScriptSource()`
- `background/navigation-utils.js``matchesSourceUrlFamily()`
- `background/tab-runtime.js` 的注册表、冲突清理、callback 清理
- `core/flow-kernel/tab-runtime.js` 的注册表、冲突清理、callback 清理
这说明当前的 source 体系不是“可扩展的来源注册表”,而是“OpenAI 时代遗留的 source 命名”。
@@ -58,10 +58,10 @@ sourceRegistry = {
'content/activation-utils.js',
'content/utils.js',
'content/operation-delay.js',
'content/auth-page-recovery.js',
'content/phone-country-utils.js',
'content/phone-auth.js',
'content/signup-page.js',
'flows/openai/content/auth-page-recovery.js',
'flows/openai/content/phone-country-utils.js',
'flows/openai/content/phone-auth.js',
'flows/openai/content/openai-auth.js',
],
cleanupScopes: ['oauth-localhost-callback'],
},
@@ -81,7 +81,7 @@ sourceRegistry = {
label: 'SUB2API 后台',
dynamicOnly: true,
familyMatcher: 'sub2api-panel-family',
injectFiles: ['content/utils.js', 'content/sub2api-panel.js'],
injectFiles: ['content/utils.js', 'flows/openai/content/sub2api-panel.js'],
},
};
```
@@ -125,7 +125,7 @@ sourceRegistry = {
driverRegistry = {
'openai-auth': {
sourceId: 'openai-auth',
driverId: 'content/signup-page',
driverId: 'flows/openai/content/openai-auth',
commands: [
'OPEN_SIGNUP',
'SUBMIT_SIGNUP_IDENTIFIER',
@@ -244,3 +244,4 @@ callbackRegistry = {
- manifest 与动态注入没有统一入口
- `content/utils.js` 的默认 source 回退会污染未来 flow
+2 -2
View File
@@ -77,8 +77,8 @@ flows/
- `phone-sms/providers/hero-sms.js`
- `phone-sms/providers/five-sim.js`
- `phone-sms/providers/registry.js`
- `content/phone-auth.js`
- `content/phone-country-utils.js`
- `flows/openai/content/phone-auth.js`
- `flows/openai/content/phone-country-utils.js`
- sidepanel 中的接码配置区
- 相关测试:`tests/phone-verification-flow.test.js``tests/five-sim-provider.test.js``tests/sidepanel-phone-verification-settings.test.js`
+2 -2
View File
@@ -7,8 +7,8 @@
定义:不离开当前步骤,在当前页面链路内恢复或替换资源。
典型位置:
- `background/steps/fetch-signup-code.js`(步骤 4 页面恢复)
- `background/steps/fetch-login-code.js` + `background/verification-flow.js`(步骤 8 邮箱轮询与重发)
- `flows/openai/background/steps/fetch-signup-code.js`(步骤 4 页面恢复)
- `flows/openai/background/steps/fetch-login-code.js` + `background/verification-flow.js`(步骤 8 邮箱轮询与重发)
- `background/phone-verification-flow.js`(步骤 9 号码轮换、重发、回到 add-phone)
常见动作:
+68
View File
@@ -0,0 +1,68 @@
(function attachMultiPageFlowsIndex(root, factory) {
root.MultiPageFlowsIndex = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createFlowsIndexModule() {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
const FLOW_ENTRY_DEFINITIONS = Object.freeze({
openai: {
id: 'openai',
path: 'flows/openai/',
},
kiro: {
id: 'kiro',
path: 'flows/kiro/',
},
});
function normalizeFlowId(value = '') {
return String(value || '').trim().toLowerCase();
}
function getRegisteredFlowIds() {
return Object.keys(FLOW_ENTRY_DEFINITIONS);
}
function getFlowEntry(flowId) {
const normalized = normalizeFlowId(flowId);
const baseEntry = FLOW_ENTRY_DEFINITIONS[normalized];
if (!baseEntry) {
return null;
}
return {
...baseEntry,
definition: normalized === 'openai'
? (rootScope.MultiPageOpenAiFlowDefinition || null)
: (rootScope.MultiPageKiroFlowDefinition || null),
workflow: normalized === 'openai'
? (rootScope.MultiPageOpenAiWorkflow || null)
: (rootScope.MultiPageKiroWorkflow || null),
};
}
function getFlowDefinition(flowId) {
return getFlowEntry(flowId)?.definition || null;
}
function getFlowDefinitions() {
const next = {};
getRegisteredFlowIds().forEach((flowId) => {
const definition = getFlowDefinition(flowId);
if (definition) {
next[flowId] = definition;
}
});
return next;
}
function getFlowWorkflow(flowId) {
return getFlowEntry(flowId)?.workflow || null;
}
return {
getFlowEntry,
getFlowDefinition,
getFlowDefinitions,
getFlowWorkflow,
getRegisteredFlowIds,
};
});
@@ -20,14 +20,15 @@
function readKiroRuntime(state = {}) {
return kiroStateApi?.ensureRuntimeState
? kiroStateApi.ensureRuntimeState(state)
: (isPlainObject(state?.kiroRuntime) ? state.kiroRuntime : {});
: (isPlainObject(state?.runtimeState?.flowState?.kiro)
? state.runtimeState.flowState.kiro
: (isPlainObject(state?.flowState?.kiro) ? state.flowState.kiro : {}));
}
function resolveKiroTargetId(state = {}, runtimeState = readKiroRuntime(state)) {
return cleanString(
state?.settingsState?.flows?.kiro?.targetId
|| state?.flows?.kiro?.targetId
|| state?.kiroTargetId
state?.settingsState?.flows?.kiro?.selectedTargetId
|| state?.targetId
|| runtimeState?.upload?.targetId
|| DEFAULT_TARGET_ID
) || DEFAULT_TARGET_ID;
@@ -46,7 +47,6 @@
return cleanString(
runtimeState?.desktopAuth?.region
|| state?.settingsState?.flows?.kiro?.targets?.[targetId]?.region
|| state?.flows?.kiro?.targets?.[targetId]?.region
|| DEFAULT_REGION
) || DEFAULT_REGION;
}
@@ -97,16 +97,40 @@
typeof kiroStateApi?.buildDefaultRuntimeState === 'function'
? kiroStateApi.buildDefaultRuntimeState()
: {},
state?.kiroRuntime || {}
isPlainObject(state?.runtimeState?.flowState?.kiro)
? state.runtimeState.flowState.kiro
: (isPlainObject(state?.flowState?.kiro) ? state.flowState.kiro : {})
);
}
function mergeRuntimePatch(currentState = {}, patch = {}) {
function buildCanonicalRuntimePatch(currentState = {}, nextRuntimeState = {}) {
if (typeof kiroStateApi?.buildRuntimeStatePatch === 'function') {
return kiroStateApi.buildRuntimeStatePatch(currentState, nextRuntimeState);
}
const baseRuntimeState = isPlainObject(currentState?.runtimeState)
? cloneValue(currentState.runtimeState)
: {};
const baseFlowState = isPlainObject(baseRuntimeState.flowState)
? cloneValue(baseRuntimeState.flowState)
: {};
return {
kiroRuntime: deepMerge(readKiroRuntime(currentState), patch),
runtimeState: {
...baseRuntimeState,
flowState: {
...baseFlowState,
kiro: deepMerge(readKiroRuntime(currentState), nextRuntimeState),
},
},
};
}
function mergeRuntimePatch(currentState = {}, patch = {}) {
return buildCanonicalRuntimePatch(
currentState,
deepMerge(readKiroRuntime(currentState), patch)
);
}
function normalizePositiveInteger(value, fallback) {
const numeric = Math.floor(Number(value));
if (Number.isInteger(numeric) && numeric > 0) {
@@ -108,20 +108,43 @@
function readKiroRuntime(state = {}) {
return kiroStateApi?.ensureRuntimeState
? kiroStateApi.ensureRuntimeState(state)
: (isPlainObject(state?.kiroRuntime) ? state.kiroRuntime : {});
: (isPlainObject(state?.runtimeState?.flowState?.kiro)
? state.runtimeState.flowState.kiro
: (isPlainObject(state?.flowState?.kiro) ? state.flowState.kiro : {}));
}
function buildCanonicalRuntimePatch(currentState = {}, nextRuntimeState = {}) {
if (typeof kiroStateApi?.buildRuntimeStatePatch === 'function') {
return kiroStateApi.buildRuntimeStatePatch(currentState, nextRuntimeState);
}
const baseRuntimeState = isPlainObject(currentState?.runtimeState)
? cloneValue(currentState.runtimeState)
: {};
const baseFlowState = isPlainObject(baseRuntimeState.flowState)
? cloneValue(baseRuntimeState.flowState)
: {};
return {
runtimeState: {
...baseRuntimeState,
flowState: {
...baseFlowState,
kiro: deepMerge(readKiroRuntime(currentState), nextRuntimeState),
},
},
};
}
function mergeRuntimePatch(currentState = {}, patch = {}) {
return {
kiroRuntime: deepMerge(readKiroRuntime(currentState), patch),
};
return buildCanonicalRuntimePatch(
currentState,
deepMerge(readKiroRuntime(currentState), patch)
);
}
function resolveKiroTargetId(state = {}) {
return cleanString(
state?.settingsState?.flows?.kiro?.targetId
|| state?.flows?.kiro?.targetId
|| state?.kiroTargetId
state?.settingsState?.flows?.kiro?.selectedTargetId
|| state?.targetId
|| readKiroRuntime(state).upload?.targetId
|| DEFAULT_TARGET_ID
) || DEFAULT_TARGET_ID;
@@ -131,9 +154,7 @@
if (targetId !== DEFAULT_TARGET_ID) {
throw new Error(`暂不支持 Kiro 发布目标:${targetId}`);
}
const nestedConfig = state?.settingsState?.flows?.kiro?.targets?.[targetId]
|| state?.flows?.kiro?.targets?.[targetId]
|| {};
const nestedConfig = state?.settingsState?.flows?.kiro?.targets?.[targetId] || {};
return {
baseUrl: cleanString(nestedConfig.baseUrl || state?.kiroRsUrl),
apiKey: normalizeKiroRsApiKey(nestedConfig.apiKey ?? state?.kiroRsKey ?? ''),
@@ -188,7 +209,6 @@
const region = normalizeRegion(
desktopAuth.region
|| state?.settingsState?.flows?.kiro?.targets?.[targetId]?.region
|| state?.flows?.kiro?.targets?.[targetId]?.region
|| DEFAULT_REGION
);
const email = cleanString(register.email || state?.email);
@@ -195,16 +195,40 @@
typeof kiroStateApi?.buildDefaultRuntimeState === 'function'
? kiroStateApi.buildDefaultRuntimeState()
: {},
state?.kiroRuntime || {}
isPlainObject(state?.runtimeState?.flowState?.kiro)
? state.runtimeState.flowState.kiro
: (isPlainObject(state?.flowState?.kiro) ? state.flowState.kiro : {})
);
}
function mergeRuntimePatch(currentState = {}, patch = {}) {
function buildCanonicalRuntimePatch(currentState = {}, nextRuntimeState = {}) {
if (typeof kiroStateApi?.buildRuntimeStatePatch === 'function') {
return kiroStateApi.buildRuntimeStatePatch(currentState, nextRuntimeState);
}
const baseRuntimeState = isPlainObject(currentState?.runtimeState)
? cloneValue(currentState.runtimeState)
: {};
const baseFlowState = isPlainObject(baseRuntimeState.flowState)
? cloneValue(baseRuntimeState.flowState)
: {};
return {
kiroRuntime: deepMerge(readKiroRuntime(currentState), patch),
runtimeState: {
...baseRuntimeState,
flowState: {
...baseFlowState,
kiro: deepMerge(readKiroRuntime(currentState), nextRuntimeState),
},
},
};
}
function mergeRuntimePatch(currentState = {}, patch = {}) {
return buildCanonicalRuntimePatch(
currentState,
deepMerge(readKiroRuntime(currentState), patch)
);
}
function getErrorMessage(error) {
return error instanceof Error ? error.message : String(error ?? '未知错误');
}
@@ -1076,7 +1100,7 @@
tokenSource: 'desktop_authorization_code_pkce',
},
upload: {
targetId: cleanString(currentState?.kiroTargetId || readKiroRuntime(currentState).upload?.targetId) || DEFAULT_TARGET_ID,
targetId: cleanString(currentState?.targetId || readKiroRuntime(currentState).upload?.targetId) || DEFAULT_TARGET_ID,
status: 'waiting_register',
error: '',
credentialId: null,
@@ -1361,7 +1385,7 @@
const pollingState = {
...currentState,
email: currentEmail,
kiroRuntime: deepMerge(readKiroRuntime(currentState), {
...mergeRuntimePatch(currentState, {
register: {
email: currentEmail,
},
@@ -58,6 +58,24 @@
return Number.isInteger(numeric) ? numeric : fallback;
}
function normalizeNullableIdentifier(value, fallback = null) {
if (value === null || value === undefined || value === '') {
return fallback;
}
if (typeof value === 'number') {
const numeric = Math.floor(value);
return Number.isInteger(numeric) ? numeric : fallback;
}
const normalized = String(value ?? '').trim();
if (!normalized) {
return fallback;
}
const numeric = Math.floor(Number(normalized));
return Number.isInteger(numeric) && String(numeric) === normalized
? numeric
: normalized;
}
function normalizeBoolean(value, fallback = false) {
if (value === true || value === false) {
return value;
@@ -174,15 +192,63 @@
targetId: normalizeString(merged.upload?.targetId, DEFAULT_TARGET_ID),
status: normalizeString(merged.upload?.status),
error: normalizeString(merged.upload?.error),
credentialId: normalizeNullableInteger(merged.upload?.credentialId),
credentialId: normalizeNullableIdentifier(merged.upload?.credentialId),
lastMessage: normalizeString(merged.upload?.lastMessage),
lastUploadedAt: Math.max(0, normalizeInteger(merged.upload?.lastUploadedAt)),
},
};
}
function buildCanonicalRuntimeStatePatch(state = {}, runtimeState = {}) {
const normalizedRuntimeState = normalizeRuntimeState(runtimeState);
const baseRuntimeState = isPlainObject(state?.runtimeState)
? cloneValue(state.runtimeState)
: {};
const baseFlowState = isPlainObject(baseRuntimeState.flowState)
? cloneValue(baseRuntimeState.flowState)
: {};
return {
...baseRuntimeState,
flowState: {
...baseFlowState,
kiro: normalizedRuntimeState,
},
};
}
function buildRuntimeStateView(runtimeState = {}) {
const normalizedFlowState = isPlainObject(runtimeState?.flowState)
? runtimeState.flowState
: {};
return {
flowState: cloneValue(normalizedFlowState),
flows: cloneValue(normalizedFlowState),
};
}
function buildRuntimeStatePatch(currentState = {}, patch = {}) {
if (!isPlainObject(patch)) {
return {};
}
const nextRuntimeState = normalizeRuntimeState(
deepMerge(ensureRuntimeState(currentState), patch)
);
return {
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
};
}
function ensureRuntimeState(state = {}) {
return normalizeRuntimeState(isPlainObject(state?.kiroRuntime) ? state.kiroRuntime : {});
const runtimeFlowState = isPlainObject(state?.runtimeState?.flowState)
? state.runtimeState.flowState
: {};
if (isPlainObject(runtimeFlowState.kiro)) {
return normalizeRuntimeState(runtimeFlowState.kiro);
}
if (isPlainObject(state?.flowState?.kiro)) {
return normalizeRuntimeState(state.flowState.kiro);
}
return buildDefaultRuntimeState();
}
function projectRuntimeFields() {
@@ -190,31 +256,30 @@
}
function buildStateView(state = {}) {
const nextRuntimeState = ensureRuntimeState(state);
const runtimeState = buildCanonicalRuntimeStatePatch(state, nextRuntimeState);
return {
...state,
kiroRuntime: ensureRuntimeState(state),
runtimeState,
...buildRuntimeStateView(runtimeState),
};
}
function buildSessionStatePatch(currentState = {}, updates = {}) {
if (!isPlainObject(updates?.kiroRuntime)) {
const runtimePatch = isPlainObject(updates?.runtimeState?.flowState?.kiro)
? updates.runtimeState.flowState.kiro
: (isPlainObject(updates?.flowState?.kiro)
? updates.flowState.kiro
: null);
if (!runtimePatch) {
return {};
}
const nextRuntimeState = normalizeRuntimeState(
deepMerge(ensureRuntimeState(currentState), updates.kiroRuntime)
);
return {
kiroRuntime: nextRuntimeState,
};
return buildRuntimeStatePatch(currentState, runtimePatch);
}
function buildRuntimeResetPatch(currentState = {}, patch = {}) {
return {
kiroRuntime: normalizeRuntimeState(
deepMerge(ensureRuntimeState(currentState), patch)
),
};
return buildRuntimeStatePatch(currentState, patch);
}
function buildStartRegisterResetPatch(currentState = {}) {
@@ -222,7 +287,7 @@
const nextRuntimeState = buildDefaultRuntimeState();
nextRuntimeState.upload.targetId = currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID;
return {
kiroRuntime: nextRuntimeState,
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
};
}
@@ -251,43 +316,45 @@
},
});
return {
kiroRuntime: nextRuntimeState,
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
};
}
function buildDesktopResetPatch(currentState = {}) {
const currentRuntimeState = ensureRuntimeState(currentState);
const nextRuntimeState = normalizeRuntimeState({
...currentRuntimeState,
session: {
...currentRuntimeState.session,
currentStage: 'desktop-authorize',
desktopTabId: null,
pageState: '',
pageUrl: '',
lastError: '',
lastWarning: '',
},
desktopAuth: buildDefaultRuntimeState().desktopAuth,
upload: {
...buildDefaultRuntimeState().upload,
targetId: currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID,
},
});
return {
kiroRuntime: normalizeRuntimeState({
...currentRuntimeState,
session: {
...currentRuntimeState.session,
currentStage: 'desktop-authorize',
desktopTabId: null,
pageState: '',
pageUrl: '',
lastError: '',
lastWarning: '',
},
desktopAuth: buildDefaultRuntimeState().desktopAuth,
upload: {
...buildDefaultRuntimeState().upload,
targetId: currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID,
},
}),
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
};
}
function buildUploadResetPatch(currentState = {}) {
const currentRuntimeState = ensureRuntimeState(currentState);
const nextRuntimeState = normalizeRuntimeState({
...currentRuntimeState,
upload: {
...buildDefaultRuntimeState().upload,
targetId: currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID,
},
});
return {
kiroRuntime: normalizeRuntimeState({
...currentRuntimeState,
upload: {
...buildDefaultRuntimeState().upload,
targetId: currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID,
},
}),
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
};
}
@@ -332,9 +399,9 @@
const nextRuntimeState = buildDefaultRuntimeState();
nextRuntimeState.upload.targetId = currentRuntimeState.upload?.targetId || DEFAULT_TARGET_ID;
return {
kiroRuntime: nextRuntimeState,
...(Object.prototype.hasOwnProperty.call(currentState, 'kiroTargetId')
? { kiroTargetId: normalizeString(currentState.kiroTargetId, DEFAULT_TARGET_ID).toLowerCase() }
runtimeState: buildCanonicalRuntimeStatePatch(currentState, nextRuntimeState),
...(Object.prototype.hasOwnProperty.call(currentState, 'targetId')
? { targetId: normalizeString(currentState.targetId, DEFAULT_TARGET_ID).toLowerCase() }
: {}),
};
}
@@ -348,6 +415,7 @@
buildDefaultRuntimeState,
buildDownstreamResetPatch,
buildFreshKeepState,
buildRuntimeStatePatch,
buildSessionStatePatch,
buildStateView,
ensureRuntimeState,
+226
View File
@@ -0,0 +1,226 @@
(function attachMultiPageKiroFlowDefinition(root, factory) {
root.MultiPageKiroFlowDefinition = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createMultiPageKiroFlowDefinition() {
function freezeDeep(entry) {
if (!entry || typeof entry !== 'object' || Object.isFrozen(entry)) {
return entry;
}
Object.getOwnPropertyNames(entry).forEach((key) => {
freezeDeep(entry[key]);
});
return Object.freeze(entry);
}
const VALUE = freezeDeep({
"id": "kiro",
"label": "Kiro",
"services": [
"account",
"email",
"proxy"
],
"capabilities": {
"supportsEmailSignup": true,
"supportsPhoneSignup": false,
"supportsPhoneVerificationSettings": false,
"supportsPlusMode": false,
"supportsContributionMode": false,
"supportsAccountContribution": true,
"supportsOpenAiOAuthContribution": false,
"contributionAdapterIds": [
"kiro-builder-id"
],
"supportedTargetIds": [
"kiro-rs"
],
"supportsLuckmail": false,
"supportsOauthTimeoutBudget": false,
"canSwitchFlow": true,
"stepDefinitionMode": "kiro",
"targetSelectorLabel": "来源"
},
"baseGroups": [
"kiro-runtime-status"
],
"targets": {
"kiro-rs": {
"id": "kiro-rs",
"label": "kiro.rs",
"groups": [
"kiro-target-kiro-rs"
]
}
},
"publicationTargets": {
"kiro-rs": {
"id": "kiro-rs",
"label": "kiro.rs"
}
},
"runtimeSources": {
"kiro-register-page": {
"flowId": "kiro",
"kind": "flow-page",
"label": "Kiro 注册页",
"readyPolicy": "top-frame-only",
"family": "kiro-register-page-family",
"driverId": "flows/kiro/content/register-page",
"cleanupScopes": [],
"detectionMatchers": [
{
"hostnames": [
"app.kiro.dev",
"kiro.dev"
]
},
{
"hostnames": [
"view.awsapps.com",
"login.awsapps.com",
"amazonaws.com"
],
"hostnameFamilies": [
"signin.aws",
"profile.aws"
],
"hostnameEndsWith": [
".amazonaws.com"
],
"matchMode": "any"
}
],
"familyMatchers": [
{
"hostnames": [
"app.kiro.dev",
"kiro.dev"
]
},
{
"hostnames": [
"view.awsapps.com",
"login.awsapps.com",
"amazonaws.com"
],
"hostnameFamilies": [
"signin.aws",
"profile.aws"
],
"hostnameEndsWith": [
".amazonaws.com"
],
"matchMode": "any"
}
]
},
"kiro-desktop-authorize": {
"flowId": "kiro",
"kind": "flow-page",
"label": "Kiro 桌面授权页",
"readyPolicy": "top-frame-only",
"family": "kiro-desktop-authorize-family",
"driverId": "flows/kiro/content/desktop-authorize-page",
"cleanupScopes": [],
"familyMatchers": [
{
"hostnames": [
"view.awsapps.com",
"login.awsapps.com",
"amazonaws.com"
],
"hostnameFamilies": [
"signin.aws",
"profile.aws"
],
"hostnameEndsWith": [
".amazonaws.com"
],
"matchMode": "any"
}
]
},
"kiro-rs-admin": {
"flowId": "kiro",
"kind": "virtual-page",
"label": "kiro.rs Admin",
"readyPolicy": "disabled",
"family": "kiro-rs-admin-family",
"driverId": null,
"cleanupScopes": [],
"familyMatchers": []
}
},
"driverDefinitions": {
"flows/kiro/content/register-page": {
"sourceId": "kiro-register-page",
"commands": [
"kiro-open-register-page",
"kiro-submit-email",
"kiro-submit-name",
"kiro-submit-verification-code",
"kiro-submit-password",
"kiro-complete-register-consent"
]
},
"flows/kiro/content/desktop-authorize-page": {
"sourceId": "kiro-desktop-authorize",
"commands": [
"kiro-complete-desktop-authorize"
]
},
"flows/kiro/background/register-runner": {
"sourceId": "kiro-register-page",
"commands": [
"kiro-open-register-page",
"kiro-submit-email",
"kiro-submit-name",
"kiro-submit-verification-code",
"kiro-submit-password",
"kiro-complete-register-consent"
]
},
"flows/kiro/background/desktop-authorize-runner": {
"sourceId": "kiro-desktop-authorize",
"commands": [
"kiro-start-desktop-authorize",
"kiro-complete-desktop-authorize"
]
},
"flows/kiro/background/publisher-kiro-rs": {
"sourceId": "kiro-rs-admin",
"commands": [
"kiro-upload-credential"
]
}
},
"defaultTargetId": "kiro-rs",
"defaultPublicationTargetId": "kiro-rs",
"defaultTargetState": {
"baseUrl": "",
"apiKey": ""
},
"settingsGroups": {
"kiro-target-kiro-rs": {
"id": "kiro-target-kiro-rs",
"label": "kiro.rs 配置",
"rowIds": [
"row-kiro-rs-url",
"row-kiro-rs-key",
"row-kiro-rs-test-status"
]
},
"kiro-runtime-status": {
"id": "kiro-runtime-status",
"label": "Kiro 运行态",
"rowIds": [
"row-kiro-web-status",
"row-kiro-login-url",
"row-kiro-upload-status"
]
}
},
"sourceAliases": {}
});
return VALUE;
});
+142
View File
@@ -0,0 +1,142 @@
(function attachMultiPageKiroWorkflow(root, factory) {
root.MultiPageKiroWorkflow = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createMultiPageKiroWorkflow() {
const KIRO_CONTRIBUTION_STEP_TITLE = '\u8d21\u732e\u4e0a\u4f20';
function freezeDeep(entry) {
if (!entry || typeof entry !== 'object' || Object.isFrozen(entry)) {
return entry;
}
Object.getOwnPropertyNames(entry).forEach((key) => {
freezeDeep(entry[key]);
});
return Object.freeze(entry);
}
const STEP_VARIANTS = freezeDeep({
"default": [
{
"id": 1,
"order": 10,
"key": "kiro-open-register-page",
"title": "打开注册页",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-open-register-page",
"flowId": "kiro"
},
{
"id": 2,
"order": 20,
"key": "kiro-submit-email",
"title": "获取邮箱并继续",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-submit-email",
"flowId": "kiro"
},
{
"id": 3,
"order": 30,
"key": "kiro-submit-name",
"title": "填写姓名并继续",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-submit-name",
"flowId": "kiro"
},
{
"id": 4,
"order": 40,
"key": "kiro-submit-verification-code",
"title": "获取验证码并继续",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-submit-verification-code",
"flowId": "kiro"
},
{
"id": 5,
"order": 50,
"key": "kiro-submit-password",
"title": "设置密码并继续",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-submit-password",
"flowId": "kiro"
},
{
"id": 6,
"order": 60,
"key": "kiro-complete-register-consent",
"title": "完成注册授权",
"sourceId": "kiro-register-page",
"driverId": "flows/kiro/background/register-runner",
"command": "kiro-complete-register-consent",
"flowId": "kiro"
},
{
"id": 7,
"order": 70,
"key": "kiro-start-desktop-authorize",
"title": "启动桌面授权",
"sourceId": "kiro-desktop-authorize",
"driverId": "flows/kiro/background/desktop-authorize-runner",
"command": "kiro-start-desktop-authorize",
"flowId": "kiro"
},
{
"id": 8,
"order": 80,
"key": "kiro-complete-desktop-authorize",
"title": "完成桌面授权",
"sourceId": "kiro-desktop-authorize",
"driverId": "flows/kiro/background/desktop-authorize-runner",
"command": "kiro-complete-desktop-authorize",
"flowId": "kiro"
},
{
"id": 9,
"order": 90,
"key": "kiro-upload-credential",
"title": "上传凭据到 kiro.rs",
"sourceId": "kiro-rs-admin",
"driverId": "flows/kiro/background/publisher-kiro-rs",
"command": "kiro-upload-credential",
"flowId": "kiro"
}
]
});
function getVariantStepDefinitions(variantKey = 'default') {
return Array.isArray(STEP_VARIANTS[variantKey]) ? STEP_VARIANTS[variantKey] : STEP_VARIANTS.default;
}
function getModeStepDefinitions() {
return getVariantStepDefinitions('default');
}
function getAllSteps() {
return getVariantStepDefinitions('default');
}
function getPlusPaymentStepTitle() {
return '';
}
function resolveStepTitle(step = {}, options = {}) {
if (step?.key === 'kiro-upload-credential' && Boolean(options?.accountContributionEnabled || options?.state?.accountContributionEnabled)) {
return KIRO_CONTRIBUTION_STEP_TITLE;
}
return step?.title || '';
}
return {
flowId: 'kiro',
getAllSteps,
getModeStepDefinitions,
getPlusPaymentStepTitle,
getVariantStepDefinitions,
resolveStepTitle,
};
});
@@ -221,14 +221,14 @@
(async () => {
try {
throwIfStep8SettledOrStopped(resolved);
signupTabId = await getTabId('signup-page');
signupTabId = await getTabId('openai-auth');
throwIfStep8SettledOrStopped(resolved);
if (signupTabId && await isTabAlive('signup-page')) {
if (signupTabId && await isTabAlive('openai-auth')) {
await chrome.tabs.update(signupTabId, { active: true });
await addStepLog(visibleStep, '已切回认证页,正在准备调试器点击...');
} else {
signupTabId = await reuseOrCreateTab('signup-page', activeState.oauthUrl);
signupTabId = await reuseOrCreateTab('openai-auth', activeState.oauthUrl);
await addStepLog(visibleStep, '已重新打开认证页,正在准备调试器点击...');
}
@@ -2,7 +2,7 @@
root.MultiPageBackgroundCpaSessionImport = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundCpaSessionImportModule() {
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'];
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/plus-checkout.js'];
function createCpaSessionImportExecutor(deps = {}) {
const {
@@ -4,8 +4,8 @@
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PAYPAL_SOURCE = 'paypal-flow';
const PLUS_CHECKOUT_ENTRY_URL = 'https://chatgpt.com/';
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'];
const PAYPAL_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/paypal-flow.js'];
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/plus-checkout.js'];
const PAYPAL_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/paypal-flow.js'];
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_PAYPAL_HOSTED = 'paypal-hosted';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
@@ -158,7 +158,7 @@
}
const timeoutMs = Math.max(1000, Number(options.timeoutMs) || 15000);
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'GET_LOGIN_AUTH_STATE',
source: 'background',
@@ -208,7 +208,7 @@
})
: 60000;
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'SUBMIT_ADD_EMAIL',
source: 'background',
@@ -439,7 +439,7 @@
}
async function ensureAuthTabForPostLoginStep(state, visibleStep) {
const authTabId = await getTabId('signup-page');
const authTabId = await getTabId('openai-auth');
if (authTabId) {
await chrome.tabs.update(authTabId, { active: true });
return authTabId;
@@ -447,7 +447,7 @@
if (!state?.oauthUrl) {
throw new Error(`步骤 ${visibleStep}:缺少登录用 OAuth 链接,请先完成刷新 OAuth 并登录。`);
}
return reuseOrCreateTab('signup-page', state.oauthUrl);
return reuseOrCreateTab('openai-auth', state.oauthUrl);
}
async function completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, options = {}) {
@@ -708,7 +708,7 @@
activeFetchLoginCodeStep = visibleStep;
activeFetchLoginCodeStepKey = 'fetch-bound-email-login-code';
const preparedState = buildBoundEmailLoginState(state, visibleStep);
const authTabId = await getTabId('signup-page');
const authTabId = await getTabId('openai-auth');
if (authTabId) {
await chrome.tabs.update(authTabId, { active: true });
@@ -716,7 +716,7 @@
if (!preparedState.oauthUrl) {
throw new Error(`步骤 ${visibleStep}:缺少登录用 OAuth 链接,请先完成绑定邮箱后刷新 OAuth 并登录。`);
}
await reuseOrCreateTab('signup-page', preparedState.oauthUrl);
await reuseOrCreateTab('openai-auth', preparedState.oauthUrl);
}
throwIfStopped();
@@ -765,7 +765,7 @@
const visibleStep = getVisibleStep(state, 8);
activeFetchLoginCodeStep = visibleStep;
activeFetchLoginCodeStepKey = 'fetch-login-code';
const authTabId = await getTabId('signup-page');
const authTabId = await getTabId('openai-auth');
if (authTabId) {
await chrome.tabs.update(authTabId, { active: true });
@@ -773,7 +773,7 @@
if (!state.oauthUrl) {
throw new Error(`缺少登录用 OAuth 链接,请先完成步骤 ${getAuthLoginStepForVisibleStep(visibleStep)}`);
}
await reuseOrCreateTab('signup-page', state.oauthUrl);
await reuseOrCreateTab('openai-auth', state.oauthUrl);
}
throwIfStopped();
@@ -188,7 +188,7 @@
async function executeStep4(state) {
const stepStartedAt = Date.now();
const verificationSessionKey = `4:${stepStartedAt}`;
const signupTabId = await getTabId('signup-page');
const signupTabId = await getTabId('openai-auth');
if (!signupTabId) {
throw new Error('认证页面标签页已关闭,无法继续步骤 4。请先执行步骤 1 或步骤 2,重新打开认证页后再试。');
@@ -228,10 +228,10 @@
try {
prepareResult = typeof sendToContentScript === 'function'
? await sendToContentScript('signup-page', prepareRequest, {
? await sendToContentScript('openai-auth', prepareRequest, {
responseTimeoutMs: prepareResponseTimeoutMs,
})
: await sendToContentScriptResilient('signup-page', prepareRequest, {
: await sendToContentScriptResilient('openai-auth', prepareRequest, {
timeoutMs: Math.max(1000, prepareTimeoutMs - (Date.now() - prepareStartAt)),
responseTimeoutMs: prepareResponseTimeoutMs,
retryDelayMs: 700,
@@ -248,7 +248,7 @@
throw error;
}
const recoverResult = await sendToContentScriptResilient('signup-page', {
const recoverResult = await sendToContentScriptResilient('openai-auth', {
type: 'RECOVER_AUTH_RETRY_PAGE',
step: 4,
source: 'background',
@@ -13,7 +13,7 @@
sendToContentScript,
setPasswordState,
setState,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
} = deps;
function normalizeSignupMethod(value = '') {
@@ -71,8 +71,8 @@
throw new Error('缺少注册账号,请先完成步骤 2。');
}
const signupTabId = await getTabId('signup-page');
if (!signupTabId || !(await isTabAlive('signup-page'))) {
const signupTabId = await getTabId('openai-auth');
if (!signupTabId || !(await isTabAlive('openai-auth'))) {
throw new Error('认证页面标签页已关闭,请先重新完成步骤 2。');
}
@@ -90,9 +90,9 @@
await setState({ accounts });
await chrome.tabs.update(signupTabId, { active: true });
await ensureContentScriptReadyOnTab('signup-page', signupTabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', signupTabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 3:密码页内容脚本未就绪,正在等待页面恢复...',
@@ -104,7 +104,7 @@
await addLog(
`步骤 3:正在填写密码,${identityLabel},密码为${state.customPassword ? '自定义' : '自动生成'}${password.length} 位)`
);
await sendToContentScript('signup-page', {
await sendToContentScript('openai-auth', {
type: 'EXECUTE_NODE',
nodeId: 'fill-password',
step: 3,
@@ -2,7 +2,7 @@
root.MultiPageBackgroundPlusCheckoutBilling = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundPlusCheckoutBillingModule() {
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'];
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/plus-checkout.js'];
const PLUS_CHECKOUT_URL_PATTERN = /^https:\/\/chatgpt\.com\/checkout(?:\/|$)/i;
const PLUS_CHECKOUT_FRAME_READY_DELAY_MS = 500;
const PLUS_CHECKOUT_SUBMIT_MAX_ATTEMPTS = 5;
@@ -15,7 +15,7 @@
await addLog(`步骤 5:已生成姓名 ${firstName} ${lastName},生日 ${year}-${month}-${day}`);
await sendToContentScript('signup-page', {
await sendToContentScript('openai-auth', {
type: 'EXECUTE_NODE',
nodeId: 'fill-profile',
step: 5,
@@ -4,7 +4,7 @@
const GOPAY_SOURCE = 'gopay-flow';
const GOPAY_OTP_SOURCE = 'gopay-otp-flow';
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const GOPAY_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/gopay-flow.js'];
const GOPAY_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/gopay-flow.js'];
const GOPAY_WAIT_TIMEOUT_MS = 120000;
const GOPAY_POLL_INTERVAL_MS = 1000;
const GOPAY_LINKING_RETRY_WAIT_MS = 15000;
@@ -301,10 +301,10 @@
});
}
await reuseOrCreateTab('signup-page', oauthUrl, { forceNew: true });
await reuseOrCreateTab('openai-auth', oauthUrl, { forceNew: true });
const result = await sendToContentScriptResilient(
'signup-page',
'openai-auth',
{
type: 'EXECUTE_NODE',
nodeId: state?.nodeId || 'oauth-login',
@@ -3,7 +3,7 @@
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundPayPalApproveModule() {
const PAYPAL_SOURCE = 'paypal-flow';
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PAYPAL_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/paypal-flow.js'];
const PAYPAL_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/paypal-flow.js'];
const PAYPAL_LOGIN_TRANSITION_TIMEOUT_MS = 30000;
const PAYPAL_LOGIN_TRANSITION_POLL_MS = 500;
@@ -2,7 +2,7 @@
root.MultiPageBackgroundSub2ApiSessionImport = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundSub2ApiSessionImportModule() {
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/plus-checkout.js'];
const PLUS_CHECKOUT_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'flows/openai/content/plus-checkout.js'];
function createSub2ApiSessionImportExecutor(deps = {}) {
const {
@@ -17,7 +17,7 @@
resolveSignupMethod = () => 'email',
resolveSignupEmailForFlow,
sendToContentScriptResilient,
SIGNUP_PAGE_INJECT_FILES,
OPENAI_AUTH_INJECT_FILES,
waitForTabStableComplete = null,
} = deps;
@@ -37,7 +37,7 @@
function isRetryableStep2TransportErrorMessage(errorLike) {
const message = getErrorMessage(errorLike);
return /Content script on signup-page did not respond in \d+s|内容脚本\s+\d+(?:\.\d+)?\s*秒内未响应|Receiving end does not exist|message channel closed|A listener indicated an asynchronous response|port closed before a response was received|did not respond in \d+s/i.test(message);
return /Content script on [\w-]+ did not respond in \d+s|内容脚本\s+\d+(?:\.\d+)?\s*秒内未响应|Receiving end does not exist|message channel closed|A listener indicated an asynchronous response|port closed before a response was received|did not respond in \d+s/i.test(message);
}
function isLikelyLoggedInChatgptHomeUrl(rawUrl) {
@@ -78,7 +78,7 @@
}
try {
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'ENSURE_SIGNUP_ENTRY_READY',
step: 2,
source: 'background',
@@ -151,7 +151,7 @@
} = options;
try {
return await sendToContentScriptResilient('signup-page', {
return await sendToContentScriptResilient('openai-auth', {
type: 'EXECUTE_NODE',
nodeId: 'submit-signup-email',
step: 2,
@@ -198,7 +198,7 @@
throw new Error('步骤 2:未找到可用的注册页标签,无法切换到手机号注册入口。');
}
const result = await sendToContentScriptResilient('signup-page', {
const result = await sendToContentScriptResilient('openai-auth', {
type: 'ENSURE_SIGNUP_PHONE_ENTRY_READY',
step: 2,
source: 'background',
@@ -232,8 +232,8 @@
}
async function ensureSignupTabForStep2() {
let signupTabId = await getTabId('signup-page');
if (!signupTabId || !(await isTabAlive('signup-page'))) {
let signupTabId = await getTabId('openai-auth');
if (!signupTabId || !(await isTabAlive('openai-auth'))) {
await addLog('步骤 2:未发现可用的注册页标签,正在重新打开 ChatGPT 官网...', 'warn');
signupTabId = (await ensureSignupEntryPageReady(2)).tabId;
} else {
@@ -243,9 +243,9 @@
signupTabId,
'步骤 2:已切换到注册页标签,正在等待页面加载完成并额外稳定 3 秒...'
);
await ensureContentScriptReadyOnTab('signup-page', signupTabId, {
inject: SIGNUP_PAGE_INJECT_FILES,
injectSource: 'signup-page',
await ensureContentScriptReadyOnTab('openai-auth', signupTabId, {
inject: OPENAI_AUTH_INJECT_FILES,
injectSource: 'openai-auth',
timeoutMs: 45000,
retryDelayMs: 900,
logMessage: '步骤 2:注册入口页内容脚本未就绪,正在等待页面恢复...',
@@ -1,4 +1,4 @@
// content/gopay-flow.js — GoPay authorization helper.
// flows/openai/content/gopay-flow.js — GoPay authorization helper.
console.log('[MultiPage:gopay-flow] Content script loaded on', location.href);
@@ -1,10 +1,10 @@
// content/signup-page.js — Content script for ChatGPT signup entry + OpenAI auth pages
// flows/openai/content/openai-auth.js — Content script for ChatGPT signup entry + OpenAI auth pages
// Injected on: auth0.openai.com, auth.openai.com, accounts.openai.com
// Dynamically injected on: chatgpt.com
console.log('[MultiPage:signup-page] Content script loaded on', location.href);
console.log('[MultiPage:openai-auth] Content script loaded on', location.href);
const SIGNUP_PAGE_LISTENER_SENTINEL = 'data-multipage-signup-page-listener';
const OPENAI_AUTH_LISTENER_SENTINEL = 'data-multipage-openai-auth-listener';
function getOperationDelayRunner() {
const rootScope = typeof window !== 'undefined' ? window : globalThis;
@@ -14,8 +14,8 @@ function getOperationDelayRunner() {
: async (_metadata, operation) => operation();
}
if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1') {
document.documentElement.setAttribute(SIGNUP_PAGE_LISTENER_SENTINEL, '1');
if (document.documentElement.getAttribute(OPENAI_AUTH_LISTENER_SENTINEL) !== '1') {
document.documentElement.setAttribute(OPENAI_AUTH_LISTENER_SENTINEL, '1');
// Listen for commands from Background
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
@@ -70,10 +70,10 @@ if (document.documentElement.getAttribute(SIGNUP_PAGE_LISTENER_SENTINEL) !== '1'
}
});
} else {
console.log('[MultiPage:signup-page] 消息监听已存在,跳过重复注册');
console.log('[MultiPage:openai-auth] 消息监听已存在,跳过重复注册');
}
const SIGNUP_PAGE_NODE_HANDLERS = Object.freeze({
const OPENAI_AUTH_NODE_HANDLERS = Object.freeze({
'submit-signup-email': (payload) => step2_clickRegister(payload),
'fill-password': (payload) => step3_fillEmailPassword(payload),
'fill-profile': (payload) => step5_fillNameBirthday(payload),
@@ -120,9 +120,9 @@ async function handleCommand(message) {
switch (message.type) {
case 'EXECUTE_NODE': {
const nodeId = String(message.nodeId || message.payload?.nodeId || '').trim();
const handler = SIGNUP_PAGE_NODE_HANDLERS[nodeId];
const handler = OPENAI_AUTH_NODE_HANDLERS[nodeId];
if (!handler) {
throw new Error(`signup-page.js 不处理节点 ${nodeId}`);
throw new Error(`openai-auth.js 不处理节点 ${nodeId}`);
}
return await handler(message.payload || {});
}
@@ -1085,7 +1085,7 @@ function logSignupPasswordDiagnostics(context, level = 'warn') {
try {
log(`${context}:密码页诊断快照:${JSON.stringify(getSignupPasswordDiagnostics())}`, level);
} catch (error) {
console.warn('[MultiPage:signup-page] failed to build signup password diagnostics:', error?.message || error);
console.warn('[MultiPage:openai-auth] failed to build signup password diagnostics:', error?.message || error);
}
}
@@ -1310,7 +1310,7 @@ async function fillSignupEmailAndContinue(email, step) {
});
} catch (error) {
if (!isStopError(error)) {
console.error('[MultiPage:signup-page] deferred signup email submit failed:', error?.message || error);
console.error('[MultiPage:openai-auth] deferred signup email submit failed:', error?.message || error);
}
}
}, 120);
@@ -2580,7 +2580,7 @@ async function submitSignupPhoneNumberAndContinue(payload = {}) {
});
} catch (error) {
if (!isStopError(error)) {
console.error('[MultiPage:signup-page] deferred signup phone submit failed:', error?.message || error);
console.error('[MultiPage:openai-auth] deferred signup phone submit failed:', error?.message || error);
}
}
}, 120);
@@ -2720,7 +2720,7 @@ async function step3_fillEmailPassword(payload) {
log('步骤 3:表单已提交');
} catch (error) {
if (!isStopError(error)) {
console.error('[MultiPage:signup-page] deferred step 3 submit failed:', error?.message || error);
console.error('[MultiPage:openai-auth] deferred step 3 submit failed:', error?.message || error);
}
}
}, 120);
@@ -1,4 +1,4 @@
// content/paypal-flow.js — PayPal login and approval helper.
// flows/openai/content/paypal-flow.js — PayPal login and approval helper.
console.log('[MultiPage:paypal-flow] Content script loaded on', location.href);
@@ -1,4 +1,4 @@
// content/plus-checkout.js — ChatGPT Plus checkout helper.
// flows/openai/content/plus-checkout.js — ChatGPT Plus checkout helper.
(function attachPlusCheckoutContentScript() {
console.log('[MultiPage:plus-checkout] Content script loaded on', location.href);
@@ -1,4 +1,4 @@
// content/sub2api-panel.js — 页内脚本:SUB2API 后台(OAuth 生成与回调提交)
// flows/openai/content/sub2api-panel.js — 页内脚本:SUB2API 后台(OAuth 生成与回调提交)
console.log('[MultiPage:sub2api-panel] Content script loaded on', location.href);
@@ -1,4 +1,4 @@
// content/vps-panel.js — Content script for CPA panel (OAuth URL request / platform verification node)
// flows/openai/content/vps-panel.js — Content script for CPA panel (OAuth URL request / platform verification node)
// Injected on: CPA panel (user-configured URL)
//
// Actual DOM structure (after login click):
@@ -1,4 +1,4 @@
// content/whatsapp-flow.js — WhatsApp Web code reader for GoPay.
// flows/openai/content/whatsapp-flow.js — WhatsApp Web code reader for GoPay.
console.log('[MultiPage:whatsapp-flow] Content script loaded on', location.href);
+425
View File
@@ -0,0 +1,425 @@
(function attachMultiPageOpenAiFlowDefinition(root, factory) {
root.MultiPageOpenAiFlowDefinition = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createMultiPageOpenAiFlowDefinition() {
function freezeDeep(entry) {
if (!entry || typeof entry !== 'object' || Object.isFrozen(entry)) {
return entry;
}
Object.getOwnPropertyNames(entry).forEach((key) => {
freezeDeep(entry[key]);
});
return Object.freeze(entry);
}
const VALUE = freezeDeep({
"id": "openai",
"label": "Codex / OpenAI",
"services": [
"account",
"email",
"proxy"
],
"capabilities": {
"supportsEmailSignup": true,
"supportsPhoneSignup": true,
"supportsPhoneVerificationSettings": true,
"supportsPlusMode": true,
"supportsContributionMode": true,
"supportsAccountContribution": true,
"supportsOpenAiOAuthContribution": true,
"contributionAdapterIds": [
"openai-oauth",
"openai-codex-file",
"openai-sub2api-file"
],
"supportedTargetIds": [
"cpa",
"sub2api",
"codex2api"
],
"supportsLuckmail": true,
"supportsOauthTimeoutBudget": true,
"canSwitchFlow": true,
"stepDefinitionMode": "openai-dynamic",
"targetSelectorLabel": "来源"
},
"baseGroups": [
"openai-plus",
"openai-phone",
"openai-oauth",
"openai-step6"
],
"targets": {
"cpa": {
"id": "cpa",
"label": "CPA 面板",
"groups": [
"openai-target-cpa"
]
},
"sub2api": {
"id": "sub2api",
"label": "SUB2API",
"groups": [
"openai-target-sub2api"
]
},
"codex2api": {
"id": "codex2api",
"label": "Codex2API",
"groups": [
"openai-target-codex2api"
]
}
},
"runtimeSources": {
"openai-auth": {
"flowId": "openai",
"kind": "flow-page",
"label": "认证页",
"readyPolicy": "allow-child-frame",
"family": "openai-auth-family",
"driverId": "flows/openai/content/openai-auth",
"cleanupScopes": [
"oauth-localhost-callback"
],
"detectionMatchers": [
{
"hostnames": [
"auth0.openai.com",
"auth.openai.com",
"accounts.openai.com"
]
}
],
"familyMatchers": [
{
"hostnames": [
"auth0.openai.com",
"auth.openai.com",
"accounts.openai.com"
]
},
{
"hostnames": [
"chatgpt.com",
"www.chatgpt.com",
"chat.openai.com"
]
}
]
},
"chatgpt": {
"flowId": "openai",
"kind": "flow-entry",
"label": "ChatGPT 首页",
"readyPolicy": "allow-child-frame",
"family": "chatgpt-entry-family",
"driverId": null,
"cleanupScopes": [],
"detectionMatchers": [
{
"hostnames": [
"chatgpt.com",
"www.chatgpt.com",
"chat.openai.com"
]
}
],
"familyMatchers": [
{
"hostnames": [
"chatgpt.com",
"www.chatgpt.com",
"chat.openai.com"
]
}
]
},
"vps-panel": {
"flowId": "openai",
"kind": "panel-page",
"label": "CPA 面板",
"readyPolicy": "allow-child-frame",
"family": "vps-panel-family",
"driverId": "flows/openai/content/vps-panel",
"cleanupScopes": [],
"familyMatchers": [
{
"originEqualsReference": true,
"pathEqualsReference": true
}
]
},
"platform-panel": {
"flowId": "openai",
"kind": "virtual-page",
"label": "平台回调面板",
"readyPolicy": "disabled",
"family": "platform-panel-family",
"driverId": "content/platform-panel",
"cleanupScopes": [],
"familyMatchers": []
},
"sub2api-panel": {
"flowId": "openai",
"kind": "panel-page",
"label": "SUB2API 后台",
"readyPolicy": "allow-child-frame",
"family": "sub2api-panel-family",
"driverId": "flows/openai/content/sub2api-panel",
"cleanupScopes": [],
"familyMatchers": [
{
"originEqualsReference": true,
"pathPrefixes": [
"/admin/accounts"
]
},
{
"originEqualsReference": true,
"pathPrefixes": [
"/login"
]
},
{
"originEqualsReference": true,
"pathEqualsOneOf": [
"/"
]
}
]
},
"codex2api-panel": {
"flowId": "openai",
"kind": "panel-page",
"label": "Codex2API 后台",
"readyPolicy": "allow-child-frame",
"family": "codex2api-panel-family",
"driverId": "flows/openai/content/sub2api-panel",
"cleanupScopes": [],
"familyMatchers": [
{
"originEqualsReference": true,
"pathPrefixes": [
"/admin/accounts"
]
},
{
"originEqualsReference": true,
"pathEqualsOneOf": [
"/admin",
"/"
]
}
]
},
"plus-checkout": {
"flowId": "openai",
"kind": "flow-page",
"label": "Plus Checkout",
"readyPolicy": "top-frame-only",
"family": "plus-checkout-family",
"driverId": "flows/openai/content/plus-checkout",
"cleanupScopes": [],
"familyMatchers": [
{
"hostnames": [
"chatgpt.com"
],
"pathPrefixes": [
"/checkout/"
]
}
]
},
"paypal-flow": {
"flowId": "openai",
"kind": "flow-page",
"label": "PayPal 授权页",
"readyPolicy": "allow-child-frame",
"family": "paypal-flow-family",
"driverId": "flows/openai/content/paypal-flow",
"cleanupScopes": [],
"familyMatchers": [
{
"hostnameEndsWith": [
"paypal.com"
]
}
]
},
"gopay-flow": {
"flowId": "openai",
"kind": "flow-page",
"label": "GoPay 授权页",
"readyPolicy": "allow-child-frame",
"family": "gopay-flow-family",
"driverId": "flows/openai/content/gopay-flow",
"cleanupScopes": [],
"familyMatchers": [
{
"hostnameRegex": "gopay|gojek",
"hostnameRegexFlags": "i"
}
]
}
},
"driverDefinitions": {
"flows/openai/content/openai-auth": {
"sourceId": "openai-auth",
"commands": [
"submit-signup-email",
"fill-password",
"fill-profile",
"oauth-login",
"submit-verification-code",
"post-login-phone-verification",
"bind-email",
"fetch-bind-email-code",
"confirm-oauth",
"detect-auth-state"
]
},
"flows/openai/content/sub2api-panel": {
"sourceId": "sub2api-panel",
"commands": [
"open-panel",
"fetch-oauth-url",
"platform-verify"
]
},
"flows/openai/content/vps-panel": {
"sourceId": "vps-panel",
"commands": [
"open-panel",
"fetch-oauth-url",
"platform-verify"
]
},
"content/platform-panel": {
"sourceId": "platform-panel",
"commands": [
"platform-verify",
"fetch-oauth-url"
]
},
"flows/openai/content/plus-checkout": {
"sourceId": "plus-checkout",
"commands": [
"plus-checkout-create",
"paypal-hosted-openai-checkout",
"plus-checkout-billing",
"plus-checkout-return"
]
},
"flows/openai/content/paypal-flow": {
"sourceId": "paypal-flow",
"commands": [
"paypal-approve",
"paypal-hosted-email",
"paypal-hosted-card",
"paypal-hosted-create-account",
"paypal-hosted-review"
]
},
"flows/openai/content/gopay-flow": {
"sourceId": "gopay-flow",
"commands": [
"gopay-subscription-confirm"
]
}
},
"defaultTargetId": "cpa",
"settingsGroups": {
"openai-target-cpa": {
"id": "openai-target-cpa",
"label": "CPA 来源",
"rowIds": [
"row-vps-url",
"row-vps-password",
"row-local-cpa-step9-mode"
]
},
"openai-target-sub2api": {
"id": "openai-target-sub2api",
"label": "SUB2API 来源",
"rowIds": [
"row-sub2api-url",
"row-sub2api-email",
"row-sub2api-password",
"row-sub2api-group",
"row-sub2api-account-priority",
"row-sub2api-default-proxy"
]
},
"openai-target-codex2api": {
"id": "openai-target-codex2api",
"label": "Codex2API 来源",
"rowIds": [
"row-codex2api-url",
"row-codex2api-admin-key"
]
},
"openai-plus": {
"id": "openai-plus",
"label": "Plus",
"rowIds": [
"row-plus-mode",
"row-plus-account-access-strategy",
"row-plus-payment-method"
]
},
"openai-phone": {
"id": "openai-phone",
"label": "接码设置",
"sectionIds": [
"phone-verification-section"
],
"rowIds": []
},
"openai-oauth": {
"id": "openai-oauth",
"label": "OAuth",
"rowIds": [
"row-oauth-flow-timeout",
"row-oauth-display"
]
},
"openai-step6": {
"id": "openai-step6",
"label": "第六步",
"rowIds": [
"row-step6-cookie-settings"
]
}
},
"targetCapabilities": {
"cpa": {
"supportsPhoneSignup": true,
"requiresPhoneSignupWarning": true,
"supportedPlusAccountAccessStrategies": [
"oauth",
"cpa_codex_session"
]
},
"sub2api": {
"supportsPhoneSignup": true,
"requiresPhoneSignupWarning": false,
"supportedPlusAccountAccessStrategies": [
"oauth",
"sub2api_codex_session"
]
},
"codex2api": {
"supportsPhoneSignup": true,
"requiresPhoneSignupWarning": false,
"supportedPlusAccountAccessStrategies": [
"oauth"
]
}
}
});
return VALUE;
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,47 @@
(function attachLegacyAccountRecordsImporter(root, factory) {
root.MultiPageLegacyAccountRecordsImporter = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createLegacyAccountRecordsImporterModule(root) {
function createAccountRecordsImporter(deps = {}) {
const createAccountRunHistoryHelpers = deps.createAccountRunHistoryHelpers
|| root.MultiPageBackgroundAccountRunHistory?.createAccountRunHistoryHelpers;
function importAccountRecords(records = []) {
if (!Array.isArray(records)) {
throw new Error('账号记录导入内容必须是数组。');
}
if (typeof createAccountRunHistoryHelpers !== 'function') {
return records
.filter((record) => Boolean(record) && typeof record === 'object' && !Array.isArray(record))
.map((record) => ({ ...record }));
}
const helpers = createAccountRunHistoryHelpers({
chrome: deps.chrome || {
storage: {
local: {
get: async () => ({}),
set: async () => {},
},
},
},
getState: deps.getState || (async () => ({})),
addLog: deps.addLog || (async () => {}),
getNodeIdByStepForState: deps.getNodeIdByStepForState || (() => ''),
normalizeAccountRunHistoryHelperBaseUrl: deps.normalizeAccountRunHistoryHelperBaseUrl
|| ((value) => String(value || '').trim()),
});
if (typeof helpers?.normalizeAccountRunHistory !== 'function') {
return [];
}
return helpers.normalizeAccountRunHistory(records);
}
return {
importAccountRecords,
};
}
return {
createAccountRecordsImporter,
};
});
+142
View File
@@ -0,0 +1,142 @@
(function attachLegacySettingsImporter(root, factory) {
root.MultiPageLegacySettingsImporter = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createLegacySettingsImporterModule(root) {
const LEGACY_TOP_LEVEL_KEYS = Object.freeze([
'panelMode',
'openaiIntegrationTargetId',
'kiroTargetId',
'stepExecutionRangeByFlow',
'kiroRuntime',
]);
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function cloneValue(value) {
if (Array.isArray(value)) {
return value.map((entry) => cloneValue(entry));
}
if (isPlainObject(value)) {
return Object.fromEntries(
Object.entries(value).map(([key, entryValue]) => [key, cloneValue(entryValue)])
);
}
return value;
}
function collectLegacyFieldHits(input = {}) {
if (!isPlainObject(input)) {
return [];
}
const hits = [];
LEGACY_TOP_LEVEL_KEYS.forEach((key) => {
if (Object.prototype.hasOwnProperty.call(input, key)) {
hits.push(key);
}
});
const settingsState = isPlainObject(input.settingsState) ? input.settingsState : {};
const openaiSettings = isPlainObject(settingsState?.flows?.openai) ? settingsState.flows.openai : {};
const kiroSettings = isPlainObject(settingsState?.flows?.kiro) ? settingsState.flows.kiro : {};
if (Object.prototype.hasOwnProperty.call(openaiSettings, 'integrationTargetId')) {
hits.push('settingsState.flows.openai.integrationTargetId');
}
if (Object.prototype.hasOwnProperty.call(openaiSettings, 'integrationTargets')) {
hits.push('settingsState.flows.openai.integrationTargets');
}
if (Object.prototype.hasOwnProperty.call(kiroSettings, 'targetId')) {
hits.push('settingsState.flows.kiro.targetId');
}
return Array.from(new Set(hits));
}
function resolveSettingsSchemaApi(deps = {}) {
if (deps.settingsSchemaApi?.normalizeSettingsState && deps.settingsSchemaApi?.buildSettingsView) {
return deps.settingsSchemaApi;
}
const settingsSchemaModule = deps.settingsSchemaModule || root.MultiPageSettingsSchema || null;
if (typeof settingsSchemaModule?.createSettingsSchema !== 'function') {
return null;
}
return settingsSchemaModule.createSettingsSchema({
flowRegistry: deps.flowRegistry || root.MultiPageFlowRegistry || null,
defaultFlowId: deps.defaultFlowId,
});
}
function buildImportInput(input = {}) {
const next = isPlainObject(input) ? cloneValue(input) : {};
const settingsState = isPlainObject(next.settingsState) ? next.settingsState : {};
const flows = isPlainObject(settingsState.flows) ? settingsState.flows : {};
const openaiState = isPlainObject(flows.openai) ? flows.openai : {};
const kiroState = isPlainObject(flows.kiro) ? flows.kiro : {};
const legacyOpenAiTargetId = String(
input?.openaiIntegrationTargetId
?? input?.panelMode
?? ''
).trim();
const legacyKiroTargetId = String(input?.kiroTargetId ?? '').trim();
const openaiHasCanonicalTarget = Object.prototype.hasOwnProperty.call(openaiState, 'selectedTargetId')
|| Object.prototype.hasOwnProperty.call(openaiState, 'integrationTargetId');
const kiroHasCanonicalTarget = Object.prototype.hasOwnProperty.call(kiroState, 'selectedTargetId')
|| Object.prototype.hasOwnProperty.call(kiroState, 'targetId');
if (!openaiHasCanonicalTarget && legacyOpenAiTargetId) {
next.settingsState = settingsState;
next.settingsState.flows = flows;
next.settingsState.flows.openai = {
...openaiState,
selectedTargetId: legacyOpenAiTargetId,
};
}
if (!kiroHasCanonicalTarget && legacyKiroTargetId) {
next.settingsState = next.settingsState || settingsState;
next.settingsState.flows = next.settingsState.flows || flows;
next.settingsState.flows.kiro = {
...kiroState,
selectedTargetId: legacyKiroTargetId,
};
}
return next;
}
function createSettingsImporter(deps = {}) {
const settingsSchemaApi = resolveSettingsSchemaApi(deps);
function importSettings(input = {}) {
if (!isPlainObject(input)) {
throw new Error('配置文件中的 settings 内容无效。');
}
if (!settingsSchemaApi?.normalizeSettingsState) {
throw new Error('设置导入器未完成初始化。');
}
const importInput = buildImportInput(input);
const normalizedState = settingsSchemaApi.normalizeSettingsState(importInput, {
activeFlowId: importInput.activeFlowId || importInput.flowId || deps.defaultFlowId,
});
return {
settingsSchemaVersion: Number(normalizedState.schemaVersion) || 0,
settingsState: cloneValue(normalizedState),
legacyFieldHits: collectLegacyFieldHits(input),
};
}
return {
collectLegacyFieldHits,
importSettings,
};
}
return {
LEGACY_TOP_LEVEL_KEYS,
collectLegacyFieldHits,
createSettingsImporter,
};
});
+29 -9
View File
@@ -49,13 +49,17 @@
],
"js": [
"content/activation-utils.js",
"shared/source-registry.js",
"flows/openai/index.js",
"flows/kiro/index.js",
"flows/index.js",
"core/flow-kernel/flow-registry.js",
"core/flow-kernel/source-registry.js",
"content/utils.js",
"content/operation-delay.js",
"content/auth-page-recovery.js",
"content/phone-country-utils.js",
"content/phone-auth.js",
"content/signup-page.js"
"flows/openai/content/auth-page-recovery.js",
"flows/openai/content/phone-country-utils.js",
"flows/openai/content/phone-auth.js",
"flows/openai/content/openai-auth.js"
],
"run_at": "document_idle"
},
@@ -66,7 +70,11 @@
],
"js": [
"content/activation-utils.js",
"shared/source-registry.js",
"flows/openai/index.js",
"flows/kiro/index.js",
"flows/index.js",
"core/flow-kernel/flow-registry.js",
"core/flow-kernel/source-registry.js",
"content/utils.js",
"content/qq-mail.js"
],
@@ -83,7 +91,11 @@
],
"js": [
"content/activation-utils.js",
"shared/source-registry.js",
"flows/openai/index.js",
"flows/kiro/index.js",
"flows/index.js",
"core/flow-kernel/flow-registry.js",
"core/flow-kernel/source-registry.js",
"content/utils.js",
"content/mail-163.js"
],
@@ -97,7 +109,11 @@
],
"js": [
"content/activation-utils.js",
"shared/source-registry.js",
"flows/openai/index.js",
"flows/kiro/index.js",
"flows/index.js",
"core/flow-kernel/flow-registry.js",
"core/flow-kernel/source-registry.js",
"content/utils.js",
"content/icloud-mail.js"
],
@@ -110,7 +126,11 @@
],
"js": [
"content/activation-utils.js",
"shared/source-registry.js",
"flows/openai/index.js",
"flows/kiro/index.js",
"flows/index.js",
"core/flow-kernel/flow-registry.js",
"core/flow-kernel/source-registry.js",
"content/utils.js",
"content/operation-delay.js",
"content/duck-mail.js"
@@ -21,21 +21,21 @@
主仓库 `codex注册扩展` 已重点阅读:
- `data/step-definitions.js`
- `shared/flow-capabilities.js`
- `shared/flow-registry.js`
- `shared/settings-schema.js`
- `core/flow-kernel/flow-capabilities.js`
- `core/flow-kernel/flow-registry.js`
- `core/flow-kernel/settings-schema.js`
- `sidepanel/sidepanel.html`
- `sidepanel/sidepanel.js`
- `background.js`
- `background/runtime-state.js`
- `core/flow-kernel/runtime-state.js`
- `background/message-router.js`
- `background/panel-bridge.js`
- `background/sub2api-api.js`
- `background/steps/platform-verify.js`
- `background/steps/create-plus-checkout.js`
- `background/steps/fill-plus-checkout.js`
- `background/steps/plus-return-confirm.js`
- `content/plus-checkout.js`
- `flows/openai/background/steps/platform-verify.js`
- `flows/openai/background/steps/create-plus-checkout.js`
- `flows/openai/background/steps/fill-plus-checkout.js`
- `flows/openai/background/steps/plus-return-confirm.js`
- `flows/openai/content/plus-checkout.js`
- `tests/step-definitions-module.test.js`
- `tests/sidepanel-plus-payment-method.test.js`
- `tests/flow-capabilities-module.test.js`
@@ -125,7 +125,7 @@
1. 通过 `background/panel-bridge.js` 请求 `background/sub2api-api.js` 生成 OAuth URL
2. 保存 `oauthUrl / sub2apiSessionId / sub2apiOAuthState`
3. 页面完成 OAuth 回调,拿到 `localhostUrl`
4. `background/steps/platform-verify.js``submitOpenAiCallback`
4. `flows/openai/background/steps/platform-verify.js``submitOpenAiCallback`
5. SUB2API 用 `exchange-code` 交换 OAuth code,再创建账号
这条链路是“完整 OAuth 账号创建”,不是导入现有 ChatGPT Web session。
@@ -140,8 +140,8 @@
现成能力位置:
- `content/plus-checkout.js`
- `background/steps/create-plus-checkout.js`
- `flows/openai/content/plus-checkout.js`
- `flows/openai/background/steps/create-plus-checkout.js`
这意味着新功能不需要再发明“如何拿 session”,只需要把这份能力接到 Plus 尾链上。
@@ -258,7 +258,7 @@
- `select-plus-account-access-strategy`
- `plus-account-access-strategy-caption`
`shared/flow-registry.js``openai-plus` 设置组应扩展为同时包含:
`core/flow-kernel/flow-registry.js``openai-plus` 设置组应扩展为同时包含:
- `row-plus-mode`
- `row-plus-payment-method`
@@ -364,13 +364,13 @@ Session 导入策略生效时,替换锚点如下:
建议新增后台执行器文件:
- `background/steps/sub2api-session-import.js`
- `flows/openai/background/steps/sub2api-session-import.js`
执行器职责:
1. 定位当前 Plus 会话页对应 tab
2. 确认当前 tab 已回到 ChatGPT / OpenAI 域名页面,且可访问 `/api/auth/session`
3. 通过现有 `content/plus-checkout.js` 能力读取:
3. 通过现有 `flows/openai/content/plus-checkout.js` 能力读取:
- `session`
- `accessToken`
4. 用现有 `background/sub2api-api.js` 登录 SUB2API
@@ -477,13 +477,13 @@ Session 导入策略生效时,替换锚点如下:
而不是去复用旧的:
- `content/sub2api-panel.js`
- `flows/openai/content/sub2api-panel.js`
原因:
1. 当前主链路已迁移到 `background/sub2api-api.js`
2. session 导入需要登录、查分组、查代理、发导入请求,这些都属于后台 API 编排
3. 继续往 `content/sub2api-panel.js` 塞逻辑会造成双实现
3. 继续往 `flows/openai/content/sub2api-panel.js` 塞逻辑会造成双实现
建议新增 API 方法:
@@ -503,7 +503,7 @@ Session 导入策略生效时,替换锚点如下:
- `background/panel-bridge.js` 继续只负责“跨 target 的 OAuth URL 申请桥接”
- session 导入不要硬塞进 `requestOAuthUrlFromPanel`
- `background/steps/platform-verify.js` 继续只处理 callback 型平台验证
- `flows/openai/background/steps/platform-verify.js` 继续只处理 callback 型平台验证
- `sub2api-session-import` 走独立后台执行器,不混进 `platform-verify`
## 5.7 后台总控与运行态设计
@@ -599,7 +599,7 @@ Session 导入策略生效时,替换锚点如下:
### 5.8.1 settings-schema
`shared/settings-schema.js` 需要在:
`core/flow-kernel/settings-schema.js` 需要在:
- `flows.openai.plus`
@@ -647,7 +647,7 @@ Session 导入策略生效时,替换锚点如下:
### 5.9.1 不要把策略可用性写死在 UI
应扩展 `shared/flow-capabilities.js`,让它输出:
应扩展 `core/flow-kernel/flow-capabilities.js`,让它输出:
- `requestedPlusAccountAccessStrategy`
- `effectivePlusAccountAccessStrategy`
@@ -821,9 +821,9 @@ session strategy 下这些逻辑全部不适用。
### 涉及文件
- `shared/settings-schema.js`
- `shared/flow-capabilities.js`
- `shared/flow-registry.js`
- `core/flow-kernel/settings-schema.js`
- `core/flow-kernel/flow-capabilities.js`
- `core/flow-kernel/flow-registry.js`
- `sidepanel/sidepanel.html`
- `sidepanel/sidepanel.js`
- `background.js`
@@ -889,10 +889,10 @@ session strategy 下这些逻辑全部不适用。
### 涉及文件
- `background/sub2api-api.js`
- `background/steps/sub2api-session-import.js`
- `background/steps/registry.js` 或后台节点注册处
- `content/plus-checkout.js`
- `background/steps/create-plus-checkout.js`
- `flows/openai/background/steps/sub2api-session-import.js`
- `core/flow-kernel/step-registry.js` 或后台节点注册处
- `flows/openai/content/plus-checkout.js`
- `flows/openai/background/steps/create-plus-checkout.js`
### 完成标准
@@ -920,9 +920,9 @@ session strategy 下这些逻辑全部不适用。
### 涉及文件
- `background.js`
- `background/runtime-state.js`
- `core/flow-kernel/runtime-state.js`
- `background/message-router.js`
- `background/steps/platform-verify.js`
- `flows/openai/background/steps/platform-verify.js`
- `background/panel-bridge.js`
- 自动运行 / auth-chain 相关测试
-582
View File
@@ -1,582 +0,0 @@
(function attachMultiPageFlowRegistry(root, factory) {
root.MultiPageFlowRegistry = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createFlowRegistryModule() {
const DEFAULT_FLOW_ID = 'openai';
const DEFAULT_OPENAI_TARGET_ID = 'cpa';
const DEFAULT_KIRO_TARGET_ID = 'kiro-rs';
const DEFAULT_KIRO_PUBLICATION_TARGET_ID = 'kiro-rs';
const DEFAULT_KIRO_RS_URL = '';
const OPENAI_TARGET_IDS = Object.freeze(['cpa', 'sub2api', 'codex2api']);
const SHARED_SERVICE_IDS = Object.freeze(['account', 'email', 'proxy']);
const DEFAULT_FLOW_CAPABILITIES = Object.freeze({
supportsEmailSignup: true,
supportsPhoneSignup: false,
supportsPhoneVerificationSettings: false,
supportsPlusMode: false,
supportsContributionMode: false,
supportsAccountContribution: false,
supportsOpenAiOAuthContribution: false,
contributionAdapterIds: [],
supportedTargetIds: [],
supportsLuckmail: false,
supportsOauthTimeoutBudget: false,
canSwitchFlow: true,
stepDefinitionMode: 'default',
targetSelectorLabel: '来源',
});
function freezeDeep(value) {
if (!value || typeof value !== 'object' || Object.isFrozen(value)) {
return value;
}
Object.getOwnPropertyNames(value).forEach((key) => {
freezeDeep(value[key]);
});
return Object.freeze(value);
}
const FLOW_DEFINITIONS = freezeDeep({
openai: {
id: 'openai',
label: 'Codex / OpenAI',
services: ['account', 'email', 'proxy'],
capabilities: {
...DEFAULT_FLOW_CAPABILITIES,
supportsPhoneSignup: true,
supportsPhoneVerificationSettings: true,
supportsPlusMode: true,
supportsContributionMode: true,
supportsAccountContribution: true,
supportsOpenAiOAuthContribution: true,
contributionAdapterIds: ['openai-oauth', 'openai-codex-file', 'openai-sub2api-file'],
supportedTargetIds: [...OPENAI_TARGET_IDS],
supportsLuckmail: true,
supportsOauthTimeoutBudget: true,
stepDefinitionMode: 'openai-dynamic',
},
baseGroups: [
'openai-plus',
'openai-phone',
'openai-oauth',
'openai-step6',
],
targets: {
cpa: {
id: 'cpa',
label: 'CPA 面板',
groups: ['openai-target-cpa'],
},
sub2api: {
id: 'sub2api',
label: 'SUB2API',
groups: ['openai-target-sub2api'],
},
codex2api: {
id: 'codex2api',
label: 'Codex2API',
groups: ['openai-target-codex2api'],
},
},
runtimeSources: {
'openai-auth': {
flowId: 'openai',
kind: 'flow-page',
label: '认证页',
readyPolicy: 'allow-child-frame',
family: 'openai-auth-family',
driverId: 'content/signup-page',
cleanupScopes: ['oauth-localhost-callback'],
},
chatgpt: {
flowId: 'openai',
kind: 'flow-entry',
label: 'ChatGPT 首页',
readyPolicy: 'allow-child-frame',
family: 'chatgpt-entry-family',
driverId: null,
cleanupScopes: [],
},
'vps-panel': {
flowId: 'openai',
kind: 'panel-page',
label: 'CPA 面板',
readyPolicy: 'allow-child-frame',
family: 'vps-panel-family',
driverId: 'content/vps-panel',
cleanupScopes: [],
},
'platform-panel': {
flowId: 'openai',
kind: 'virtual-page',
label: '平台回调面板',
readyPolicy: 'disabled',
family: 'platform-panel-family',
driverId: 'content/platform-panel',
cleanupScopes: [],
},
'sub2api-panel': {
flowId: 'openai',
kind: 'panel-page',
label: 'SUB2API 后台',
readyPolicy: 'allow-child-frame',
family: 'sub2api-panel-family',
driverId: 'content/sub2api-panel',
cleanupScopes: [],
},
'codex2api-panel': {
flowId: 'openai',
kind: 'panel-page',
label: 'Codex2API 后台',
readyPolicy: 'allow-child-frame',
family: 'codex2api-panel-family',
driverId: 'content/sub2api-panel',
cleanupScopes: [],
},
'plus-checkout': {
flowId: 'openai',
kind: 'flow-page',
label: 'Plus Checkout',
readyPolicy: 'top-frame-only',
family: 'plus-checkout-family',
driverId: 'content/plus-checkout',
cleanupScopes: [],
},
'paypal-flow': {
flowId: 'openai',
kind: 'flow-page',
label: 'PayPal 授权页',
readyPolicy: 'allow-child-frame',
family: 'paypal-flow-family',
driverId: 'content/paypal-flow',
cleanupScopes: [],
},
'gopay-flow': {
flowId: 'openai',
kind: 'flow-page',
label: 'GoPay 授权页',
readyPolicy: 'allow-child-frame',
family: 'gopay-flow-family',
driverId: 'content/gopay-flow',
cleanupScopes: [],
},
},
driverDefinitions: {
'content/signup-page': {
sourceId: 'openai-auth',
commands: [
'submit-signup-email',
'fill-password',
'fill-profile',
'oauth-login',
'submit-verification-code',
'post-login-phone-verification',
'bind-email',
'fetch-bind-email-code',
'confirm-oauth',
'detect-auth-state',
],
},
'content/sub2api-panel': {
sourceId: 'sub2api-panel',
commands: ['open-panel', 'fetch-oauth-url', 'platform-verify'],
},
'content/vps-panel': {
sourceId: 'vps-panel',
commands: ['open-panel', 'fetch-oauth-url', 'platform-verify'],
},
'content/platform-panel': {
sourceId: 'platform-panel',
commands: ['platform-verify', 'fetch-oauth-url'],
},
'content/plus-checkout': {
sourceId: 'plus-checkout',
commands: [
'plus-checkout-create',
'paypal-hosted-openai-checkout',
'plus-checkout-billing',
'plus-checkout-return',
],
},
'content/paypal-flow': {
sourceId: 'paypal-flow',
commands: [
'paypal-approve',
'paypal-hosted-email',
'paypal-hosted-card',
'paypal-hosted-create-account',
'paypal-hosted-review',
],
},
'content/gopay-flow': {
sourceId: 'gopay-flow',
commands: ['gopay-subscription-confirm'],
},
},
},
kiro: {
id: 'kiro',
label: 'Kiro',
services: ['account', 'email', 'proxy'],
capabilities: {
...DEFAULT_FLOW_CAPABILITIES,
supportsAccountContribution: true,
contributionAdapterIds: ['kiro-builder-id'],
supportedTargetIds: [DEFAULT_KIRO_TARGET_ID],
stepDefinitionMode: 'kiro',
},
baseGroups: [
'kiro-runtime-status',
],
targets: {
'kiro-rs': {
id: 'kiro-rs',
label: 'kiro.rs',
groups: ['kiro-target-kiro-rs'],
},
},
publicationTargets: {
'kiro-rs': {
id: 'kiro-rs',
label: 'kiro.rs',
},
},
runtimeSources: {
'kiro-register-page': {
flowId: 'kiro',
kind: 'flow-page',
label: 'Kiro 注册页',
readyPolicy: 'top-frame-only',
family: 'kiro-register-page-family',
driverId: 'content/kiro/register-page',
cleanupScopes: [],
},
'kiro-desktop-authorize': {
flowId: 'kiro',
kind: 'flow-page',
label: 'Kiro 桌面授权页',
readyPolicy: 'top-frame-only',
family: 'kiro-desktop-authorize-family',
driverId: 'content/kiro/desktop-authorize-page',
cleanupScopes: [],
},
'kiro-rs-admin': {
flowId: 'kiro',
kind: 'virtual-page',
label: 'kiro.rs Admin',
readyPolicy: 'disabled',
family: 'kiro-rs-admin-family',
driverId: null,
cleanupScopes: [],
},
},
driverDefinitions: {
'content/kiro/register-page': {
sourceId: 'kiro-register-page',
commands: [
'kiro-open-register-page',
'kiro-submit-email',
'kiro-submit-name',
'kiro-submit-verification-code',
'kiro-submit-password',
'kiro-complete-register-consent',
],
},
'content/kiro/desktop-authorize-page': {
sourceId: 'kiro-desktop-authorize',
commands: [
'kiro-complete-desktop-authorize',
],
},
'background/kiro-register': {
sourceId: 'kiro-register-page',
commands: [
'kiro-open-register-page',
'kiro-submit-email',
'kiro-submit-name',
'kiro-submit-verification-code',
'kiro-submit-password',
'kiro-complete-register-consent',
],
},
'background/kiro-desktop-authorize': {
sourceId: 'kiro-desktop-authorize',
commands: [
'kiro-start-desktop-authorize',
'kiro-complete-desktop-authorize',
],
},
'background/kiro-publisher-kiro-rs': {
sourceId: 'kiro-rs-admin',
commands: [
'kiro-upload-credential',
],
},
},
},
});
const SETTINGS_GROUP_DEFINITIONS = freezeDeep({
'service-account': {
id: 'service-account',
label: '账户',
rowIds: ['row-custom-password'],
},
'service-email': {
id: 'service-email',
label: '邮箱服务',
},
'service-proxy': {
id: 'service-proxy',
label: 'IP 代理',
sectionIds: ['ip-proxy-section'],
},
'openai-target-cpa': {
id: 'openai-target-cpa',
label: 'CPA 来源',
rowIds: ['row-vps-url', 'row-vps-password', 'row-local-cpa-step9-mode'],
},
'openai-target-sub2api': {
id: 'openai-target-sub2api',
label: 'SUB2API 来源',
rowIds: [
'row-sub2api-url',
'row-sub2api-email',
'row-sub2api-password',
'row-sub2api-group',
'row-sub2api-account-priority',
'row-sub2api-default-proxy',
],
},
'openai-target-codex2api': {
id: 'openai-target-codex2api',
label: 'Codex2API 来源',
rowIds: ['row-codex2api-url', 'row-codex2api-admin-key'],
},
'openai-plus': {
id: 'openai-plus',
label: 'Plus',
rowIds: ['row-plus-mode', 'row-plus-account-access-strategy', 'row-plus-payment-method'],
},
'openai-phone': {
id: 'openai-phone',
label: '接码设置',
sectionIds: ['phone-verification-section'],
rowIds: [],
},
'openai-oauth': {
id: 'openai-oauth',
label: 'OAuth',
rowIds: ['row-oauth-flow-timeout', 'row-oauth-display'],
},
'openai-step6': {
id: 'openai-step6',
label: '第六步',
rowIds: ['row-step6-cookie-settings'],
},
'kiro-target-kiro-rs': {
id: 'kiro-target-kiro-rs',
label: 'kiro.rs 配置',
rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key', 'row-kiro-rs-test-status'],
},
'kiro-runtime-status': {
id: 'kiro-runtime-status',
label: 'Kiro 运行态',
rowIds: ['row-kiro-web-status', 'row-kiro-login-url', 'row-kiro-upload-status'],
},
});
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
const normalized = String(value || '').trim().toLowerCase();
if (normalized && Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, normalized)) {
return normalized;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
return Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, fallbackValue)
? fallbackValue
: DEFAULT_FLOW_ID;
}
function getRegisteredFlowIds() {
return Object.keys(FLOW_DEFINITIONS);
}
function getFlowDefinition(flowId) {
return FLOW_DEFINITIONS[normalizeFlowId(flowId)] || FLOW_DEFINITIONS[DEFAULT_FLOW_ID];
}
function getFlowLabel(flowId) {
return getFlowDefinition(flowId)?.label || normalizeFlowId(flowId);
}
function getDefaultTargetId(flowId) {
return normalizeFlowId(flowId) === 'kiro'
? DEFAULT_KIRO_TARGET_ID
: DEFAULT_OPENAI_TARGET_ID;
}
function normalizeOpenAiTargetId(value = '', fallback = DEFAULT_OPENAI_TARGET_ID) {
const normalized = String(value || '').trim().toLowerCase();
if (OPENAI_TARGET_IDS.includes(normalized)) {
return normalized;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
return OPENAI_TARGET_IDS.includes(fallbackValue)
? fallbackValue
: DEFAULT_OPENAI_TARGET_ID;
}
function normalizeKiroTargetId(value = '', fallback = DEFAULT_KIRO_TARGET_ID) {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === DEFAULT_KIRO_TARGET_ID) {
return normalized;
}
const fallbackValue = String(fallback || '').trim().toLowerCase();
return fallbackValue === DEFAULT_KIRO_TARGET_ID
? fallbackValue
: DEFAULT_KIRO_TARGET_ID;
}
function normalizeTargetId(flowId, targetId = '', fallback = undefined) {
const normalizedFlowId = normalizeFlowId(flowId);
if (normalizedFlowId === 'kiro') {
return normalizeKiroTargetId(
targetId,
fallback || DEFAULT_KIRO_TARGET_ID
);
}
return normalizeOpenAiTargetId(
targetId,
fallback || DEFAULT_OPENAI_TARGET_ID
);
}
function getTargetDefinitions(flowId) {
return getFlowDefinition(flowId)?.targets || {};
}
function getTargetDefinition(flowId, targetId) {
const normalizedFlowId = normalizeFlowId(flowId);
const normalizedTargetId = normalizeTargetId(
normalizedFlowId,
targetId,
getDefaultTargetId(normalizedFlowId)
);
return getTargetDefinitions(normalizedFlowId)[normalizedTargetId] || null;
}
function getTargetOptions(flowId) {
return Object.values(getTargetDefinitions(flowId));
}
function getTargetLabel(flowId, targetId) {
return getTargetDefinition(flowId, targetId)?.label
|| normalizeTargetId(flowId, targetId);
}
function getPublicationTargetDefinitions(flowId) {
return getFlowDefinition(flowId)?.publicationTargets || {};
}
function getPublicationTargetDefinition(flowId, publicationTargetId) {
const normalizedFlowId = normalizeFlowId(flowId);
const normalizedPublicationTargetId = String(
publicationTargetId || (
normalizedFlowId === 'kiro'
? DEFAULT_KIRO_PUBLICATION_TARGET_ID
: ''
)
).trim().toLowerCase();
return getPublicationTargetDefinitions(normalizedFlowId)[normalizedPublicationTargetId] || null;
}
function getFlowCapabilities(flowId) {
return {
...DEFAULT_FLOW_CAPABILITIES,
...(getFlowDefinition(flowId)?.capabilities || {}),
};
}
function getVisibleGroupIds(flowId, targetId, options = {}) {
const normalizedFlowId = normalizeFlowId(flowId);
const flowDefinition = getFlowDefinition(normalizedFlowId);
const normalizedTargetId = normalizeTargetId(
normalizedFlowId,
targetId,
getDefaultTargetId(normalizedFlowId)
);
const targetDefinition = getTargetDefinition(
normalizedFlowId,
normalizedTargetId
);
const includeSharedServices = options?.includeSharedServices !== false;
const serviceGroups = includeSharedServices
? (Array.isArray(flowDefinition?.services)
? flowDefinition.services.map((serviceId) => `service-${serviceId}`)
: [])
: [];
return Array.from(new Set([
...(Array.isArray(flowDefinition?.baseGroups) ? flowDefinition.baseGroups : []),
...(Array.isArray(targetDefinition?.groups) ? targetDefinition.groups : []),
...serviceGroups,
]));
}
function getSettingsGroupDefinition(groupId) {
const normalizedGroupId = String(groupId || '').trim();
return SETTINGS_GROUP_DEFINITIONS[normalizedGroupId] || null;
}
function getSettingsGroupDefinitions() {
return SETTINGS_GROUP_DEFINITIONS;
}
function getRuntimeSourceDefinitions() {
const next = {};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition.runtimeSources || {});
});
return next;
}
function getDriverDefinitions() {
const next = {};
Object.values(FLOW_DEFINITIONS).forEach((flowDefinition) => {
Object.assign(next, flowDefinition.driverDefinitions || {});
});
return next;
}
return {
DEFAULT_FLOW_CAPABILITIES,
DEFAULT_FLOW_ID,
DEFAULT_KIRO_TARGET_ID,
DEFAULT_KIRO_PUBLICATION_TARGET_ID,
DEFAULT_KIRO_RS_URL,
DEFAULT_OPENAI_TARGET_ID,
FLOW_DEFINITIONS,
OPENAI_TARGET_IDS,
SETTINGS_GROUP_DEFINITIONS,
SHARED_SERVICE_IDS,
getDriverDefinitions,
getDefaultTargetId,
getFlowCapabilities,
getFlowDefinition,
getFlowLabel,
getPublicationTargetDefinition,
getPublicationTargetDefinitions,
getRegisteredFlowIds,
getRuntimeSourceDefinitions,
getSettingsGroupDefinition,
getSettingsGroupDefinitions,
getTargetDefinition,
getTargetDefinitions,
getTargetLabel,
getTargetOptions,
getVisibleGroupIds,
normalizeFlowId,
normalizeKiroTargetId,
normalizeOpenAiTargetId,
normalizeTargetId,
};
});
-406
View File
@@ -1,406 +0,0 @@
(function attachMultiPageSourceRegistry(root, factory) {
root.MultiPageSourceRegistry = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createSourceRegistryModule() {
const rootScope = typeof self !== 'undefined' ? self : globalThis;
const flowRegistryApi = rootScope.MultiPageFlowRegistry || {};
const SOURCE_ALIASES = Object.freeze({
'signup-page': 'openai-auth',
});
const SHARED_SOURCE_DEFINITIONS = Object.freeze({
'qq-mail': {
flowId: null,
kind: 'mail-provider',
label: 'QQ 邮箱',
readyPolicy: 'top-frame-only',
family: 'qq-mail-family',
driverId: 'content/qq-mail',
cleanupScopes: [],
},
'mail-163': {
flowId: null,
kind: 'mail-provider',
label: '163 邮箱',
readyPolicy: 'top-frame-only',
family: 'mail-163-family',
driverId: 'content/mail-163',
cleanupScopes: [],
},
'gmail-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Gmail 邮箱',
readyPolicy: 'top-frame-only',
family: 'gmail-mail-family',
driverId: 'content/gmail-mail',
cleanupScopes: [],
},
'icloud-mail': {
flowId: null,
kind: 'mail-provider',
label: 'iCloud 邮箱',
readyPolicy: 'allow-child-frame',
family: 'icloud-mail-family',
driverId: 'content/icloud-mail',
cleanupScopes: [],
},
'inbucket-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Inbucket 邮箱',
readyPolicy: 'top-frame-only',
family: 'inbucket-mail-family',
driverId: 'content/inbucket-mail',
cleanupScopes: [],
},
'mail-2925': {
flowId: null,
kind: 'mail-provider',
label: '2925 邮箱',
readyPolicy: 'top-frame-only',
family: 'mail-2925-family',
driverId: 'content/mail-2925',
cleanupScopes: [],
},
'duck-mail': {
flowId: null,
kind: 'mail-provider',
label: 'Duck 邮箱',
readyPolicy: 'allow-child-frame',
family: 'duck-mail-family',
driverId: 'content/duck-mail',
cleanupScopes: [],
},
'unknown-source': {
flowId: null,
kind: 'unknown',
label: '未知来源',
readyPolicy: 'disabled',
family: 'unknown-family',
driverId: null,
cleanupScopes: [],
},
});
const SHARED_DRIVER_DEFINITIONS = Object.freeze({
'content/qq-mail': {
sourceId: 'qq-mail',
commands: ['POLL_EMAIL'],
},
'content/mail-163': {
sourceId: 'mail-163',
commands: ['POLL_EMAIL'],
},
'content/gmail-mail': {
sourceId: 'gmail-mail',
commands: ['POLL_EMAIL'],
},
'content/icloud-mail': {
sourceId: 'icloud-mail',
commands: ['POLL_EMAIL'],
},
'content/mail-2925': {
sourceId: 'mail-2925',
commands: ['POLL_EMAIL'],
},
'content/duck-mail': {
sourceId: 'duck-mail',
commands: ['FETCH_ALIAS_EMAIL'],
},
});
const CLEANUP_SCOPE_OWNERS = Object.freeze({
'oauth-localhost-callback': 'openai-auth',
});
const AUTH_PAGE_HOSTS = new Set(['auth0.openai.com', 'auth.openai.com', 'accounts.openai.com']);
const ENTRY_PAGE_HOSTS = new Set(['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com']);
const CHILD_FRAME_BLOCKED_SOURCES = new Set([
'qq-mail',
'mail-163',
'gmail-mail',
'mail-2925',
'inbucket-mail',
'plus-checkout',
'kiro-register-page',
'kiro-desktop-authorize',
]);
function normalizeHostname(hostname = '') {
return String(hostname || '').trim().toLowerCase();
}
function matchesNamedHostFamily(hostname = '', family = '') {
const normalizedHost = normalizeHostname(hostname);
const normalizedFamily = normalizeHostname(family);
if (!normalizedHost || !normalizedFamily) {
return false;
}
return normalizedHost === normalizedFamily
|| normalizedHost.endsWith(`.${normalizedFamily}`)
|| normalizedHost.startsWith(`${normalizedFamily}.`)
|| normalizedHost.includes(`.${normalizedFamily}.`);
}
function isKiroWebHost(hostname = '') {
const normalized = normalizeHostname(hostname);
return normalized === 'app.kiro.dev'
|| normalized === 'kiro.dev';
}
function isKiroAwsAuthHost(hostname = '') {
const normalized = normalizeHostname(hostname);
return normalized === 'view.awsapps.com'
|| normalized === 'login.awsapps.com'
|| matchesNamedHostFamily(normalized, 'signin.aws')
|| matchesNamedHostFamily(normalized, 'profile.aws')
|| normalized === 'amazonaws.com'
|| normalized.endsWith('.amazonaws.com');
}
function isKiroRegisterHost(hostname = '') {
return isKiroWebHost(hostname) || isKiroAwsAuthHost(hostname);
}
function getRuntimeSourceDefinitions() {
return {
...(typeof flowRegistryApi.getRuntimeSourceDefinitions === 'function'
? flowRegistryApi.getRuntimeSourceDefinitions()
: {}),
...SHARED_SOURCE_DEFINITIONS,
};
}
function getDriverDefinitions() {
return {
...(typeof flowRegistryApi.getDriverDefinitions === 'function'
? flowRegistryApi.getDriverDefinitions()
: {}),
...SHARED_DRIVER_DEFINITIONS,
};
}
function createSourceRegistry() {
const SOURCE_DEFINITIONS = getRuntimeSourceDefinitions();
const DRIVER_DEFINITIONS = getDriverDefinitions();
function parseUrlSafely(rawUrl) {
if (!rawUrl) return null;
try {
return new URL(rawUrl);
} catch {
return null;
}
}
function normalizeSourceId(source) {
return String(source || '').trim();
}
function resolveCanonicalSource(source) {
const normalized = normalizeSourceId(source);
if (!normalized) return '';
return SOURCE_ALIASES[normalized] || normalized;
}
function getAliasKeysForCanonicalSource(source) {
const canonical = resolveCanonicalSource(source);
return Object.keys(SOURCE_ALIASES).filter((alias) => SOURCE_ALIASES[alias] === canonical);
}
function getSourceKeys(source) {
const normalized = normalizeSourceId(source);
const canonical = resolveCanonicalSource(normalized);
return Array.from(new Set([
canonical,
...getAliasKeysForCanonicalSource(canonical),
normalized,
].filter(Boolean)));
}
function getSourceMeta(source) {
const canonical = resolveCanonicalSource(source);
const definition = SOURCE_DEFINITIONS[canonical];
if (!definition) {
return null;
}
return {
id: canonical,
aliases: getAliasKeysForCanonicalSource(canonical),
...definition,
};
}
function getSourceLabel(source) {
return getSourceMeta(source)?.label || normalizeSourceId(source) || '未知来源';
}
function getDriverIdForSource(source) {
return getSourceMeta(source)?.driverId || null;
}
function getDriverMeta(sourceOrDriverId) {
const directDriverId = normalizeSourceId(sourceOrDriverId);
const driverId = Object.prototype.hasOwnProperty.call(DRIVER_DEFINITIONS, directDriverId)
? directDriverId
: getDriverIdForSource(sourceOrDriverId);
if (!driverId || !Object.prototype.hasOwnProperty.call(DRIVER_DEFINITIONS, driverId)) {
return null;
}
return {
id: driverId,
...DRIVER_DEFINITIONS[driverId],
};
}
function driverAcceptsCommand(sourceOrDriverId, command) {
const normalizedCommand = normalizeSourceId(command);
if (!normalizedCommand) {
return false;
}
const driver = getDriverMeta(sourceOrDriverId);
return Array.isArray(driver?.commands) && driver.commands.includes(normalizedCommand);
}
function isSignupPageHost(hostname = '') {
return AUTH_PAGE_HOSTS.has(normalizeHostname(hostname));
}
function isSignupEntryHost(hostname = '') {
return ENTRY_PAGE_HOSTS.has(normalizeHostname(hostname));
}
function is163MailHost(hostname = '') {
const normalized = normalizeHostname(hostname);
return normalized === 'mail.163.com'
|| normalized.endsWith('.mail.163.com')
|| normalized === 'mail.126.com'
|| normalized.endsWith('.mail.126.com')
|| normalized === 'webmail.vip.163.com';
}
function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
const candidate = parseUrlSafely(candidateUrl);
if (!candidate) return false;
const canonical = resolveCanonicalSource(source);
const reference = parseUrlSafely(referenceUrl);
switch (canonical) {
case 'openai-auth':
return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname);
case 'chatgpt':
return isSignupEntryHost(candidate.hostname);
case 'duck-mail':
return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/');
case 'qq-mail':
return candidate.hostname === 'mail.qq.com' || candidate.hostname === 'wx.mail.qq.com';
case 'mail-163':
return is163MailHost(candidate.hostname);
case 'gmail-mail':
return candidate.hostname === 'mail.google.com';
case 'icloud-mail':
return candidate.hostname === 'www.icloud.com'
|| candidate.hostname === 'www.icloud.com.cn';
case 'inbucket-mail':
return Boolean(reference)
&& candidate.origin === reference.origin
&& candidate.pathname.startsWith('/m/');
case 'mail-2925':
return candidate.hostname === '2925.com' || candidate.hostname === 'www.2925.com';
case 'vps-panel':
return Boolean(reference)
&& candidate.origin === reference.origin
&& candidate.pathname === reference.pathname;
case 'sub2api-panel':
return Boolean(reference)
&& candidate.origin === reference.origin
&& (
candidate.pathname.startsWith('/admin/accounts')
|| candidate.pathname.startsWith('/login')
|| candidate.pathname === '/'
);
case 'codex2api-panel':
return Boolean(reference)
&& candidate.origin === reference.origin
&& (
candidate.pathname.startsWith('/admin/accounts')
|| candidate.pathname === '/admin'
|| candidate.pathname === '/'
);
case 'plus-checkout':
return candidate.hostname === 'chatgpt.com'
&& candidate.pathname.startsWith('/checkout/');
case 'paypal-flow':
return candidate.hostname.endsWith('paypal.com');
case 'gopay-flow':
return /gopay|gojek/i.test(candidate.hostname);
case 'kiro-register-page':
return isKiroRegisterHost(candidate.hostname);
case 'kiro-desktop-authorize':
return isKiroAwsAuthHost(candidate.hostname);
default:
return false;
}
}
function detectSourceFromLocation({
injectedSource,
url = '',
hostname = '',
} = {}) {
if (injectedSource) return resolveCanonicalSource(injectedSource);
const normalizedHostname = String(hostname || '').toLowerCase();
const normalizedUrl = String(url || '');
if (isSignupPageHost(normalizedHostname)) return 'openai-auth';
if (normalizedHostname === 'mail.qq.com' || normalizedHostname === 'wx.mail.qq.com') return 'qq-mail';
if (is163MailHost(normalizedHostname)) return 'mail-163';
if (normalizedHostname === 'mail.google.com') return 'gmail-mail';
if (normalizedHostname === 'www.icloud.com' || normalizedHostname === 'www.icloud.com.cn') return 'icloud-mail';
if (normalizedUrl.includes('duckduckgo.com/email/settings/autofill')) return 'duck-mail';
if (normalizedUrl.includes('2925.com')) return 'mail-2925';
if (isKiroRegisterHost(normalizedHostname)) return 'kiro-register-page';
if (isSignupEntryHost(normalizedHostname)) return 'chatgpt';
return 'unknown-source';
}
function shouldReportReadyForFrame(source, isChildFrame) {
const canonical = resolveCanonicalSource(source);
const readyPolicy = getSourceMeta(canonical)?.readyPolicy || 'allow-child-frame';
if (readyPolicy === 'disabled') return false;
if (!isChildFrame) return true;
if (readyPolicy === 'top-frame-only') return false;
if (CHILD_FRAME_BLOCKED_SOURCES.has(canonical)) return false;
return true;
}
function getCleanupOwnerSource(cleanupScope) {
return resolveCanonicalSource(CLEANUP_SCOPE_OWNERS[String(cleanupScope || '').trim()] || '');
}
return {
detectSourceFromLocation,
driverAcceptsCommand,
getCleanupOwnerSource,
getDriverIdForSource,
getDriverMeta,
getSourceKeys,
getSourceLabel,
getSourceMeta,
is163MailHost,
isSignupEntryHost,
isSignupPageHost,
matchesSourceUrlFamily,
parseUrlSafely,
resolveCanonicalSource,
shouldReportReadyForFrame,
};
}
return {
createSourceRegistry,
};
});
+6 -8
View File
@@ -77,7 +77,7 @@
}
function getContributionSource(currentState = getLatestState()) {
return normalizeContributionSource(currentState.contributionSource || currentState.panelMode);
return normalizeContributionSource(currentState.contributionSource || getActiveTargetId(currentState));
}
function getContributionSourceLabel(currentState = getLatestState()) {
@@ -107,10 +107,8 @@
if (selectedTargetId) {
return selectedTargetId;
}
if (activeFlowId === 'kiro') {
return normalizeString(currentState.kiroTargetId || currentState.targetId || 'kiro-rs').toLowerCase() || 'kiro-rs';
}
return normalizeString(currentState.openaiIntegrationTargetId || currentState.panelMode || currentState.targetId || 'cpa').toLowerCase() || 'cpa';
return normalizeString(currentState.targetId).toLowerCase()
|| (activeFlowId === 'kiro' ? 'kiro-rs' : 'cpa');
}
function applySelectedFlowToState(nextState = {}, flowId = 'openai', targetId = '') {
@@ -122,14 +120,14 @@
...baseState,
activeFlowId: selectedFlowId,
flowId: selectedFlowId,
panelMode: selectedTargetId || normalizeString(baseState.panelMode).toLowerCase() || 'cpa',
targetId: selectedTargetId || normalizeString(baseState.targetId).toLowerCase() || 'cpa',
};
}
return {
...baseState,
activeFlowId: selectedFlowId,
flowId: selectedFlowId,
kiroTargetId: selectedTargetId || normalizeString(baseState.kiroTargetId || baseState.targetId).toLowerCase() || 'kiro-rs',
targetId: selectedTargetId || normalizeString(baseState.targetId).toLowerCase() || 'kiro-rs',
};
}
@@ -164,7 +162,7 @@
if (registry?.resolveSidepanelCapabilities) {
return Boolean(registry.resolveSidepanelCapabilities({
activeFlowId: getActiveFlowId(currentState),
panelMode: currentState?.panelMode,
targetId: getActiveTargetId(currentState),
state: currentState,
})?.canShowContributionMode);
}
+9 -4
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
@@ -1876,10 +1876,15 @@
<script src="../hotmail-utils.js"></script>
<script src="../luckmail-utils.js"></script>
<script src="../yyds-mail-utils.js"></script>
<script src="../shared/flow-registry.js"></script>
<script src="../flows/openai/index.js"></script>
<script src="../flows/openai/workflow.js"></script>
<script src="../flows/kiro/index.js"></script>
<script src="../flows/kiro/workflow.js"></script>
<script src="../flows/index.js"></script>
<script src="../core/flow-kernel/flow-registry.js"></script>
<script src="../shared/contribution-registry.js"></script>
<script src="../shared/settings-schema.js"></script>
<script src="../shared/flow-capabilities.js"></script>
<script src="../core/flow-kernel/settings-schema.js"></script>
<script src="../core/flow-kernel/flow-capabilities.js"></script>
<script src="../data/step-definitions.js"></script>
<script src="update-service.js"></script>
<script src="contribution-content-update-service.js"></script>
+432 -149
View File
@@ -2002,13 +2002,44 @@ async function openConfirmModalWithOption({
async function openPlusManualConfirmationDialog(options = {}) {
const method = String(options.method || '').trim().toLowerCase();
const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase();
const panelMode = String(latestState?.panelMode || latestState?.openaiIntegrationTargetId || '').trim().toLowerCase();
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai';
if (normalizedFlowId === 'openai') {
const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase();
return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa';
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow
: ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs'));
const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId
: ((value = '') => {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === 'sub2api' || normalized === 'codex2api') {
return normalized;
}
return 'cpa';
});
const targetId = normalizePlusStrategyTargetIdSafe(
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(latestState, activeFlowId)
: normalizeTargetIdForFlowSafe(
activeFlowId,
latestState?.targetId || '',
getDefaultTargetIdForFlowSafe(activeFlowId)
)
);
const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase();
const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled);
const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase();
const useSub2ApiSessionImport = plusModeEnabled
&& activeFlowId === 'openai'
&& panelMode === 'sub2api'
&& targetId === 'sub2api'
&& signupMethod === 'email'
&& plusAccountAccessStrategy === 'sub2api_codex_session';
const continuationActionLabel = useSub2ApiSessionImport
@@ -2042,13 +2073,44 @@ async function syncPlusManualConfirmationDialog() {
const step = Number(latestState?.plusManualConfirmationStep) || 0;
const method = String(latestState?.plusManualConfirmationMethod || '').trim().toLowerCase();
const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase();
const panelMode = String(latestState?.panelMode || latestState?.openaiIntegrationTargetId || '').trim().toLowerCase();
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai';
if (normalizedFlowId === 'openai') {
const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase();
return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa';
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow
: ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs'));
const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId
: ((value = '') => {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === 'sub2api' || normalized === 'codex2api') {
return normalized;
}
return 'cpa';
});
const targetId = normalizePlusStrategyTargetIdSafe(
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(latestState, activeFlowId)
: normalizeTargetIdForFlowSafe(
activeFlowId,
latestState?.targetId || '',
getDefaultTargetIdForFlowSafe(activeFlowId)
)
);
const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase();
const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled);
const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase();
const useSub2ApiSessionImport = plusModeEnabled
&& activeFlowId === 'openai'
&& panelMode === 'sub2api'
&& targetId === 'sub2api'
&& signupMethod === 'email'
&& plusAccountAccessStrategy === 'sub2api_codex_session';
const continuationActionLabel = useSub2ApiSessionImport
@@ -2159,10 +2221,10 @@ function getContributionContentFlowId(state = latestState) {
function getContributionContentTargetId(state = latestState) {
const flowId = getContributionContentFlowId(state);
if (flowId === 'kiro') {
return String(state?.kiroTargetId || state?.targetId || 'kiro-rs').trim().toLowerCase() || 'kiro-rs';
if (typeof getSelectedTargetIdForState === 'function') {
return getSelectedTargetIdForState(state, flowId);
}
return String(state?.openaiIntegrationTargetId || state?.panelMode || state?.targetId || 'cpa').trim().toLowerCase() || 'cpa';
return normalizeTargetIdForFlow(flowId, state?.targetId || '', getDefaultTargetIdForFlow(flowId));
}
function openNewUserGuidePrompt() {
@@ -2255,29 +2317,54 @@ function shouldWarnAutoRunFallbackRisk(totalRuns, autoRunSkipFailures) {
return totalRuns >= AUTO_RUN_FALLBACK_RISK_WARNING_MIN_RUNS;
}
function shouldWarnCpaPhoneSignup(signupMethod = null, panelMode = null) {
function shouldWarnCpaPhoneSignup(signupMethod = null, targetId = null) {
const defaultFlowId = typeof DEFAULT_ACTIVE_FLOW_ID === 'string' ? DEFAULT_ACTIVE_FLOW_ID : 'openai';
const resolvedSignupMethod = normalizeSignupMethod(
signupMethod ?? (
typeof getSelectedSignupMethod === 'function'
? getSelectedSignupMethod()
: DEFAULT_SIGNUP_METHOD
)
)
);
const resolvedPanelMode = normalizePanelMode(
panelMode ?? (
typeof getSelectedPanelMode === 'function'
? getSelectedPanelMode()
: 'cpa'
)
const activeFlowId = typeof getSelectedFlowId === 'function'
? getSelectedFlowId(latestState)
: (
typeof normalizeFlowId === 'function'
? normalizeFlowId(latestState?.activeFlowId || latestState?.flowId || defaultFlowId)
: (String(latestState?.activeFlowId || latestState?.flowId || defaultFlowId).trim().toLowerCase() || defaultFlowId)
);
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, value = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || defaultFlowId;
if (normalizedFlowId === defaultFlowId) {
return normalizePanelMode(value || fallback);
}
const normalizedValue = String(value || '').trim().toLowerCase();
return normalizedValue || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const resolvedTargetId = normalizeTargetIdForFlowSafe(
activeFlowId,
targetId ?? (
typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId)
: getSelectedTargetIdForState(latestState, activeFlowId)
),
typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow(activeFlowId)
: (activeFlowId === defaultFlowId ? 'cpa' : 'kiro-rs')
);
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: resolvedPanelMode,
activeFlowId,
targetId: resolvedTargetId,
signupMethod: resolvedSignupMethod,
state: {
...(typeof latestState !== 'undefined' ? latestState : {}),
panelMode: resolvedPanelMode,
activeFlowId,
flowId: activeFlowId,
targetId: resolvedTargetId,
signupMethod: resolvedSignupMethod,
},
})
@@ -2288,12 +2375,14 @@ function shouldWarnCpaPhoneSignup(signupMethod = null, panelMode = null) {
}) || null;
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: typeof latestState !== 'undefined' ? latestState?.activeFlowId : '',
panelMode: resolvedPanelMode,
activeFlowId,
targetId: resolvedTargetId,
signupMethod: resolvedSignupMethod,
state: {
...(typeof latestState !== 'undefined' ? latestState : {}),
panelMode: resolvedPanelMode,
activeFlowId,
flowId: activeFlowId,
targetId: resolvedTargetId,
signupMethod: resolvedSignupMethod,
},
})
@@ -2305,7 +2394,7 @@ function shouldWarnCpaPhoneSignup(signupMethod = null, panelMode = null) {
}
return resolvedSignupMethod === SIGNUP_METHOD_PHONE
&& resolvedPanelMode === 'cpa'
&& resolvedTargetId === 'cpa'
&& !isCpaPhoneSignupPromptDismissed();
}
@@ -2327,11 +2416,11 @@ async function confirmCpaPhoneSignupIfNeeded(options = {}) {
const signupMethod = Object.prototype.hasOwnProperty.call(options, 'signupMethod')
? options.signupMethod
: null;
const panelMode = Object.prototype.hasOwnProperty.call(options, 'panelMode')
? options.panelMode
const targetId = Object.prototype.hasOwnProperty.call(options, 'targetId')
? options.targetId
: null;
if (!shouldWarnCpaPhoneSignup(signupMethod, panelMode)) {
if (!shouldWarnCpaPhoneSignup(signupMethod, targetId)) {
return true;
}
@@ -2559,6 +2648,18 @@ function getKiroUploadStatusLabel(value = '') {
}
}
function getKiroRuntimeState(state = {}) {
const runtimeState = state?.runtimeState?.flowState?.kiro;
if (runtimeState && typeof runtimeState === 'object' && !Array.isArray(runtimeState)) {
return runtimeState;
}
const flowState = state?.flowState?.kiro;
if (flowState && typeof flowState === 'object' && !Array.isArray(flowState)) {
return flowState;
}
return {};
}
function setKiroRsConnectionTestStatus(message = '') {
const nextText = String(message || '').trim() || '未测试';
kiroRsConnectionTestStatusText = nextText;
@@ -2873,6 +2974,18 @@ function syncLatestState(nextState) {
nodeStatuses: mergedNodeStatuses,
};
const activeFlowId = typeof normalizeFlowId === 'function'
? normalizeFlowId(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID, DEFAULT_ACTIVE_FLOW_ID)
: (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
const schema = typeof getSettingsSchema === 'function' ? getSettingsSchema() : null;
const selectedTargetId = schema?.getSelectedTargetId
? schema.getSelectedTargetId(latestState, activeFlowId)
: (String(latestState?.targetId || '').trim().toLowerCase()
|| (activeFlowId === 'kiro' ? 'kiro-rs' : 'cpa'));
latestState.targetId = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow(activeFlowId, selectedTargetId)
: selectedTargetId;
renderAccountRecords(latestState);
}
@@ -3053,7 +3166,12 @@ function getRequestedPlusAccountAccessStrategy(state = latestState) {
if (typeof selectPanelMode !== 'undefined' && selectPanelMode?.value) {
return selectPanelMode.value;
}
return state?.panelMode || state?.openaiIntegrationTargetId || 'cpa';
return getSelectedTargetIdForState({
...(latestState || {}),
...(state || {}),
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
flowId: DEFAULT_ACTIVE_FLOW_ID,
}, DEFAULT_ACTIVE_FLOW_ID);
};
const resolveStrategyForTarget = typeof resolvePlusAccountAccessStrategyForTarget === 'function'
? resolvePlusAccountAccessStrategyForTarget
@@ -3136,7 +3254,38 @@ function resolvePlusManualContinuationActionLabelFromState(state = latestState)
const activeFlowId = String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase();
const signupMethod = normalizeSignupMethod(state?.resolvedSignupMethod || state?.signupMethod || DEFAULT_SIGNUP_METHOD);
const plusModeEnabled = state?.plusModeEnabled === undefined ? true : Boolean(state?.plusModeEnabled);
const targetId = normalizePlusStrategyTargetId(state?.panelMode || state?.openaiIntegrationTargetId || 'cpa');
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
if (normalizedFlowId === DEFAULT_ACTIVE_FLOW_ID) {
const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase();
return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa';
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow
: ((flowId = DEFAULT_ACTIVE_FLOW_ID) => (String(flowId || '').trim().toLowerCase() === DEFAULT_ACTIVE_FLOW_ID ? 'cpa' : 'kiro-rs'));
const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId
: ((value = '') => {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === 'sub2api' || normalized === 'codex2api') {
return normalized;
}
return 'cpa';
});
const targetId = normalizePlusStrategyTargetIdSafe(
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(state, activeFlowId)
: normalizeTargetIdForFlowSafe(
activeFlowId,
state?.targetId || '',
getDefaultTargetIdForFlowSafe(activeFlowId)
)
);
const strategy = normalizePlusAccountAccessStrategy(state?.plusAccountAccessStrategy || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY);
const effectiveStrategy = plusModeEnabled && activeFlowId === DEFAULT_ACTIVE_FLOW_ID && signupMethod === SIGNUP_METHOD_EMAIL
? strategy
@@ -4478,12 +4627,63 @@ function collectSettingsPayload() {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa';
});
const rawPanelMode = normalizePanelModeSafe(selectPanelMode?.value || latestState?.panelMode || 'cpa');
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || defaultFlowId;
if (normalizedFlowId === defaultFlowId) {
return normalizePanelModeSafe(targetId || fallback);
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const resolvePlusAccountAccessStrategyForTargetSafe = typeof resolvePlusAccountAccessStrategyForTarget === 'function'
? resolvePlusAccountAccessStrategyForTarget
: ((value = '', targetId = '') => {
const oauthStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH !== 'undefined'
? PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH
: 'oauth';
const sub2apiSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION !== 'undefined'
? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION
: 'sub2api_codex_session';
const cpaSessionStrategyValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION !== 'undefined'
? PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION
: 'cpa_codex_session';
const sessionUiValue = typeof PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI !== 'undefined'
? PLUS_ACCOUNT_ACCESS_STRATEGY_CODEX_SESSION_UI
: 'codex_session';
const normalizedValue = String(value || '').trim().toLowerCase();
const isSessionImport = normalizedValue === sessionUiValue
|| normalizedValue === sub2apiSessionStrategyValue
|| normalizedValue === cpaSessionStrategyValue;
if (!isSessionImport) {
return oauthStrategyValue;
}
const normalizedTargetId = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId(targetId)
: normalizePanelModeSafe(targetId || '');
if (normalizedTargetId === 'sub2api') {
return sub2apiSessionStrategyValue;
}
if (normalizedTargetId === 'cpa') {
return cpaSessionStrategyValue;
}
return oauthStrategyValue;
});
const selectedTargetId = typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId)
: (activeFlowId === defaultFlowId
? rawPanelMode
: String(selectPanelMode?.value || latestState?.kiroTargetId || 'kiro-rs').trim().toLowerCase() || 'kiro-rs');
: normalizeTargetIdForFlowSafe(
activeFlowId,
selectPanelMode?.value || latestState?.targetId || '',
typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow(activeFlowId)
: (activeFlowId === defaultFlowId ? 'cpa' : 'kiro-rs')
);
const openAiTargetId = normalizePanelModeSafe(
activeFlowId === defaultFlowId
? selectedTargetId
: getSelectedPanelMode(latestState)
);
const rawPlusModeEnabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
? Boolean(inputPlusModeEnabled.checked)
: Boolean(latestState?.plusModeEnabled);
@@ -4495,14 +4695,12 @@ function collectSettingsPayload() {
? resolveCurrentSidepanelCapabilities({
activeFlowId,
targetId: selectedTargetId,
panelMode: rawPanelMode,
signupMethod: selectedSignupMethod,
state: {
...(latestState || {}),
activeFlowId,
...(activeFlowId === defaultFlowId
? { panelMode: rawPanelMode }
: { kiroTargetId: selectedTargetId }),
flowId: activeFlowId,
targetId: selectedTargetId,
plusModeEnabled: rawPlusModeEnabled,
plusAccountAccessStrategy: requestedPlusAccountAccessStrategy,
phoneVerificationEnabled: rawPhoneVerificationEnabled,
@@ -4517,15 +4715,13 @@ function collectSettingsPayload() {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId,
panelMode: rawPanelMode,
targetId: selectedTargetId,
signupMethod: selectedSignupMethod,
state: {
...(latestState || {}),
activeFlowId,
...(activeFlowId === defaultFlowId
? { panelMode: rawPanelMode }
: { kiroTargetId: selectedTargetId }),
flowId: activeFlowId,
targetId: selectedTargetId,
plusModeEnabled: rawPlusModeEnabled,
plusAccountAccessStrategy: requestedPlusAccountAccessStrategy,
phoneVerificationEnabled: rawPhoneVerificationEnabled,
@@ -4534,8 +4730,12 @@ function collectSettingsPayload() {
})
: null;
})();
const effectivePanelMode = capabilityState?.effectivePanelMode || capabilityState?.panelMode || rawPanelMode;
const effectiveTargetId = capabilityState?.effectiveTargetId || selectedTargetId;
const effectiveOpenAiTargetId = normalizePanelModeSafe(
activeFlowId === defaultFlowId
? effectiveTargetId
: openAiTargetId
);
const effectivePlusModeEnabled = capabilityState
? Boolean(capabilityState.runtimeLocks?.plusModeEnabled)
: rawPlusModeEnabled;
@@ -4623,16 +4823,7 @@ function collectSettingsPayload() {
});
return {
activeFlowId,
...(accountContributionEnabled ? {} : {
...(activeFlowId === defaultFlowId ? { panelMode: effectivePanelMode } : {}),
}),
kiroTargetId: normalizeKiroTargetIdSafe(
'kiro',
activeFlowId === 'kiro'
? effectiveTargetId
: (latestState?.kiroTargetId || 'kiro-rs'),
'kiro-rs'
),
targetId: effectiveTargetId,
kiroRsUrl: currentKiroRsUrlValue !== null
? (currentKiroRsUrlValue || defaultKiroRsUrl)
: (String(latestState?.kiroRsUrl || defaultKiroRsUrl).trim() || defaultKiroRsUrl),
@@ -4677,7 +4868,9 @@ function collectSettingsPayload() {
codex2apiAdminKey: inputCodex2ApiAdminKey.value.trim(),
plusModeEnabled: effectivePlusModeEnabled,
plusPaymentMethod,
plusAccountAccessStrategy: requestedPlusAccountAccessStrategy,
plusAccountAccessStrategy: activeFlowId === defaultFlowId
? resolvePlusAccountAccessStrategyForTargetSafe(requestedPlusAccountAccessStrategy, effectiveOpenAiTargetId)
: requestedPlusAccountAccessStrategy,
hostedCheckoutVerificationUrl: typeof inputHostedCheckoutVerificationUrl !== 'undefined' && inputHostedCheckoutVerificationUrl
? String(inputHostedCheckoutVerificationUrl.value || '').trim()
: String(latestState?.hostedCheckoutVerificationUrl || '').trim(),
@@ -8683,12 +8876,9 @@ function getSelectedTargetIdForState(state = latestState, flowId = getSelectedFl
if (schema?.getSelectedTargetId) {
return schema.getSelectedTargetId(state || {}, normalizedFlowId);
}
if (normalizedFlowId === DEFAULT_ACTIVE_FLOW_ID) {
return normalizePanelMode(state?.panelMode || getDefaultTargetIdForFlow(normalizedFlowId));
}
return normalizeTargetIdForFlow(
normalizedFlowId,
state?.kiroTargetId || '',
state?.targetId || '',
getDefaultTargetIdForFlow(normalizedFlowId)
);
}
@@ -8698,14 +8888,9 @@ function getSelectedTargetId(flowId = getSelectedFlowId()) {
const selectedValue = typeof selectPanelMode !== 'undefined' && selectPanelMode
? selectPanelMode.value
: '';
if (normalizedFlowId === DEFAULT_ACTIVE_FLOW_ID) {
return normalizePanelMode(
selectedValue || latestState?.panelMode || getDefaultTargetIdForFlow(normalizedFlowId)
);
}
return normalizeTargetIdForFlow(
normalizedFlowId,
selectedValue || latestState?.kiroTargetId || '',
selectedValue || latestState?.targetId || '',
getDefaultTargetIdForFlow(normalizedFlowId)
);
}
@@ -8857,24 +9042,15 @@ function resolveCurrentSidepanelCapabilities(options = {}) {
};
const targetId = options?.targetId !== undefined
? options.targetId
: (activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? (options?.panelMode ?? state?.panelMode)
: (options?.kiroTargetId ?? state?.kiroTargetId));
if (activeFlowId === DEFAULT_ACTIVE_FLOW_ID) {
state.panelMode = normalizePanelMode(
targetId || state?.panelMode || getDefaultTargetIdForFlow(activeFlowId)
);
} else {
state.kiroTargetId = normalizeTargetIdForFlow(
activeFlowId,
targetId || state?.kiroTargetId || '',
getDefaultTargetIdForFlow(activeFlowId)
);
}
: (state?.targetId ?? getSelectedTargetIdForState(state, activeFlowId));
state.targetId = normalizeTargetIdForFlow(
activeFlowId,
targetId || state?.targetId || '',
getDefaultTargetIdForFlow(activeFlowId)
);
return registry.resolveSidepanelCapabilities({
activeFlowId,
panelMode: state?.panelMode,
targetId: activeFlowId === DEFAULT_ACTIVE_FLOW_ID ? state?.panelMode : state?.kiroTargetId,
targetId: state.targetId,
signupMethod: options?.signupMethod ?? state?.signupMethod,
state,
});
@@ -8887,7 +9063,7 @@ function resolveStepDefinitionCapabilityState(state = latestState, options = {})
};
const capabilityState = resolveCurrentSidepanelCapabilities({
activeFlowId: options?.activeFlowId ?? nextState?.activeFlowId,
panelMode: options?.panelMode ?? nextState?.panelMode,
targetId: options?.targetId ?? nextState?.targetId,
signupMethod: options?.signupMethod ?? nextState?.signupMethod,
state: nextState,
});
@@ -8905,16 +9081,35 @@ function resolveStepDefinitionCapabilityState(state = latestState, options = {})
};
}
function getSelectedPanelMode() {
function getSelectedPanelMode(state = latestState) {
const resolvedPanelMode = normalizePanelMode(
typeof selectPanelMode !== 'undefined' && selectPanelMode
? selectPanelMode.value
: (typeof latestState !== 'undefined' ? latestState?.panelMode : '')
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState({
...(typeof latestState !== 'undefined' ? latestState : {}),
...(state || {}),
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
flowId: DEFAULT_ACTIVE_FLOW_ID,
}, DEFAULT_ACTIVE_FLOW_ID)
: (
typeof selectPanelMode !== 'undefined' && selectPanelMode
? selectPanelMode.value
: (typeof state !== 'undefined' ? state?.targetId : '')
)
);
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({ panelMode: resolvedPanelMode })
? resolveCurrentSidepanelCapabilities({
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
targetId: resolvedPanelMode,
state: {
...(typeof latestState !== 'undefined' ? latestState : {}),
...(state || {}),
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
flowId: DEFAULT_ACTIVE_FLOW_ID,
targetId: resolvedPanelMode,
},
})
: null;
return capabilityState?.effectivePanelMode || capabilityState?.panelMode || resolvedPanelMode;
return normalizePanelMode(capabilityState?.effectiveTargetId || resolvedPanelMode);
}
function getSelectedSignupMethod() {
@@ -8943,7 +9138,9 @@ function canSelectPhoneSignupMethod() {
: Boolean(latestState?.accountContributionEnabled);
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode,
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
state: {
...(typeof latestState !== 'undefined' ? latestState : {}),
phoneVerificationEnabled: phoneEnabled,
@@ -8959,7 +9156,9 @@ function canSelectPhoneSignupMethod() {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: typeof latestState !== 'undefined' ? latestState?.activeFlowId : '',
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : (latestState?.panelMode || 'cpa'),
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
state: {
...(typeof latestState !== 'undefined' ? latestState : {}),
phoneVerificationEnabled: phoneEnabled,
@@ -9061,7 +9260,9 @@ function updatePhoneVerificationSettingsUI() {
: Boolean(latestState?.plusModeEnabled);
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode,
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod,
state: {
...(latestState || {}),
@@ -9077,7 +9278,9 @@ function updatePhoneVerificationSettingsUI() {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: latestState?.activeFlowId,
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : (latestState?.panelMode || 'cpa'),
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod,
state: {
...(latestState || {}),
@@ -9366,7 +9569,9 @@ function updatePlusModeUI() {
: false;
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode,
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
state: {
...(latestState || {}),
plusModeEnabled: rawEnabled,
@@ -9381,7 +9586,9 @@ function updatePlusModeUI() {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: latestState?.activeFlowId,
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : (latestState?.panelMode || 'cpa'),
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
state: {
...(latestState || {}),
plusModeEnabled: rawEnabled,
@@ -9403,10 +9610,8 @@ function updatePlusModeUI() {
|| requestedPlusAccountAccessStrategy
|| oauthStrategyValue;
const effectiveTargetId = resolveStrategyTargetId(
capabilityState?.effectivePanelMode
|| capabilityState?.effectiveTargetId
|| capabilityState?.panelMode
|| (typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode)
capabilityState?.effectiveTargetId
|| (typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode(latestState) : latestState?.targetId)
|| 'cpa'
);
const method = enabled ? getSelectedPlusPaymentMethod() : defaultMethod;
@@ -9711,7 +9916,12 @@ function syncSignupPhoneInputFromState(state = latestState) {
: (String(rawSignupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email');
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: state?.panelMode || latestState?.panelMode,
targetId: typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState({
...(latestState || {}),
...(state || {}),
}, state?.activeFlowId || latestState?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID)
: (state?.targetId || latestState?.targetId),
signupMethod: selectedMethod,
state: {
...(latestState || {}),
@@ -9727,7 +9937,12 @@ function syncSignupPhoneInputFromState(state = latestState) {
return registry?.resolveSidepanelCapabilities
? registry.resolveSidepanelCapabilities({
activeFlowId: state?.activeFlowId || latestState?.activeFlowId,
panelMode: state?.panelMode || latestState?.panelMode,
targetId: typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState({
...(latestState || {}),
...(state || {}),
}, state?.activeFlowId || latestState?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID)
: (state?.targetId || latestState?.targetId),
signupMethod: selectedMethod,
state: {
...(latestState || {}),
@@ -9925,13 +10140,44 @@ async function openPlusManualConfirmationDialog(options = {}) {
const method = String(options.method || '').trim().toLowerCase();
const gopayValue = typeof PLUS_PAYMENT_METHOD_GOPAY !== 'undefined' ? PLUS_PAYMENT_METHOD_GOPAY : 'gopay';
const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase();
const panelMode = String(latestState?.panelMode || latestState?.openaiIntegrationTargetId || '').trim().toLowerCase();
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai';
if (normalizedFlowId === 'openai') {
const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase();
return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa';
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow
: ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs'));
const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId
: ((value = '') => {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === 'sub2api' || normalized === 'codex2api') {
return normalized;
}
return 'cpa';
});
const targetId = normalizePlusStrategyTargetIdSafe(
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(latestState, activeFlowId)
: normalizeTargetIdForFlowSafe(
activeFlowId,
latestState?.targetId || '',
getDefaultTargetIdForFlowSafe(activeFlowId)
)
);
const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase();
const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled);
const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase();
const useSub2ApiSessionImport = plusModeEnabled
&& activeFlowId === 'openai'
&& panelMode === 'sub2api'
&& targetId === 'sub2api'
&& signupMethod === 'email'
&& plusAccountAccessStrategy === 'sub2api_codex_session';
const continuationActionLabel = useSub2ApiSessionImport
@@ -9996,13 +10242,44 @@ async function syncPlusManualConfirmationDialog() {
const step = Number(latestState?.plusManualConfirmationStep) || 0;
const method = String(latestState?.plusManualConfirmationMethod || '').trim().toLowerCase();
const activeFlowId = String(latestState?.activeFlowId || latestState?.flowId || 'openai').trim().toLowerCase();
const panelMode = String(latestState?.panelMode || latestState?.openaiIntegrationTargetId || '').trim().toLowerCase();
const normalizeTargetIdForFlowSafe = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow
: ((flowId, targetId = '', fallback = '') => {
const normalizedFlowId = String(flowId || '').trim().toLowerCase() || 'openai';
if (normalizedFlowId === 'openai') {
const normalizedTargetId = String(targetId || fallback || '').trim().toLowerCase();
return normalizedTargetId === 'sub2api' || normalizedTargetId === 'codex2api' ? normalizedTargetId : 'cpa';
}
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
return normalizedTargetId || String(fallback || '').trim().toLowerCase() || 'kiro-rs';
});
const getDefaultTargetIdForFlowSafe = typeof getDefaultTargetIdForFlow === 'function'
? getDefaultTargetIdForFlow
: ((flowId = 'openai') => (String(flowId || '').trim().toLowerCase() === 'openai' ? 'cpa' : 'kiro-rs'));
const normalizePlusStrategyTargetIdSafe = typeof normalizePlusStrategyTargetId === 'function'
? normalizePlusStrategyTargetId
: ((value = '') => {
const normalized = String(value || '').trim().toLowerCase();
if (normalized === 'sub2api' || normalized === 'codex2api') {
return normalized;
}
return 'cpa';
});
const targetId = normalizePlusStrategyTargetIdSafe(
typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(latestState, activeFlowId)
: normalizeTargetIdForFlowSafe(
activeFlowId,
latestState?.targetId || '',
getDefaultTargetIdForFlowSafe(activeFlowId)
)
);
const signupMethod = String(latestState?.resolvedSignupMethod || latestState?.signupMethod || 'email').trim().toLowerCase();
const plusModeEnabled = latestState?.plusModeEnabled === undefined ? true : Boolean(latestState.plusModeEnabled);
const plusAccountAccessStrategy = String(latestState?.plusAccountAccessStrategy || 'oauth').trim().toLowerCase();
const useSub2ApiSessionImport = plusModeEnabled
&& activeFlowId === 'openai'
&& panelMode === 'sub2api'
&& targetId === 'sub2api'
&& signupMethod === 'email'
&& plusAccountAccessStrategy === 'sub2api_codex_session';
const continuationActionLabel = useSub2ApiSessionImport
@@ -10523,7 +10800,7 @@ function syncStepDefinitionsFromUiState(stateOverrides = {}) {
const stepDefinitionState = typeof resolveStepDefinitionCapabilityState === 'function'
? resolveStepDefinitionCapabilityState(nextState, {
activeFlowId: nextState?.activeFlowId,
panelMode: nextState?.panelMode,
targetId: nextState?.targetId,
signupMethod: nextState?.signupMethod,
state: nextState,
})
@@ -10616,7 +10893,7 @@ function applySettingsState(state) {
? syncFlowSelectorsFromState(state)
: {
activeFlowId: String(state?.activeFlowId || state?.flowId || defaultActiveFlowId).trim().toLowerCase() || defaultActiveFlowId,
targetId: String(state?.panelMode || 'cpa').trim().toLowerCase() || 'cpa',
targetId: String(state?.targetId || 'cpa').trim().toLowerCase() || 'cpa',
};
if (typeof applyOperationDelayState === 'function') {
applyOperationDelayState(state);
@@ -10748,24 +11025,38 @@ function applySettingsState(state) {
if (typeof displayKiroRsTestStatus !== 'undefined' && displayKiroRsTestStatus) {
displayKiroRsTestStatus.textContent = kiroRsConnectionTestStatusText;
}
const resolveKiroRuntimeState = typeof getKiroRuntimeState === 'function'
? getKiroRuntimeState
: ((value = {}) => {
const runtimeState = value?.runtimeState?.flowState?.kiro;
if (runtimeState && typeof runtimeState === 'object' && !Array.isArray(runtimeState)) {
return runtimeState;
}
const flowState = value?.flowState?.kiro;
if (flowState && typeof flowState === 'object' && !Array.isArray(flowState)) {
return flowState;
}
return {};
});
const kiroRuntimeState = resolveKiroRuntimeState(state);
if (typeof displayKiroWebStatus !== 'undefined' && displayKiroWebStatus) {
const kiroWebStatus = String(
state?.kiroRuntime?.webAuth?.status
|| state?.kiroRuntime?.register?.status
kiroRuntimeState?.webAuth?.status
|| kiroRuntimeState?.register?.status
|| ''
).trim();
displayKiroWebStatus.textContent = kiroWebStatus || '未开始';
}
if (typeof displayKiroLoginUrl !== 'undefined' && displayKiroLoginUrl) {
const kiroLoginUrl = String(
state?.kiroRuntime?.register?.loginUrl
kiroRuntimeState?.register?.loginUrl
|| ''
).trim();
displayKiroLoginUrl.textContent = kiroLoginUrl || '未打开';
}
if (typeof displayKiroUploadStatus !== 'undefined' && displayKiroUploadStatus) {
const kiroUploadStatus = String(
state?.kiroRuntime?.upload?.status
kiroRuntimeState?.upload?.status
|| ''
).trim();
displayKiroUploadStatus.textContent = getKiroUploadStatusLabel(kiroUploadStatus);
@@ -12404,7 +12695,9 @@ function updateMailProviderUI() {
: latestState?.icloudHostPreference;
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode,
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
state: latestState || {},
})
: null;
@@ -13048,23 +13341,16 @@ function updatePanelModeUI() {
: normalizeFlowId(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID);
const targetId = typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId)
: (activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? normalizePanelMode(selectPanelMode?.value || latestState?.panelMode || 'cpa')
: String(selectPanelMode?.value || latestState?.kiroTargetId || 'kiro-rs').trim().toLowerCase() || 'kiro-rs');
const rawPanelMode = activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? normalizePanelMode(targetId || latestState?.panelMode || 'cpa')
: normalizePanelMode(latestState?.panelMode || 'cpa');
: normalizeTargetIdForFlow(activeFlowId, selectPanelMode?.value || latestState?.targetId || '', getDefaultTargetIdForFlow(activeFlowId));
const capabilityState = typeof resolveCurrentSidepanelCapabilities === 'function'
? resolveCurrentSidepanelCapabilities({
activeFlowId,
targetId,
panelMode: rawPanelMode,
state: {
...(latestState || {}),
activeFlowId,
...(activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? { panelMode: rawPanelMode }
: { kiroTargetId: targetId }),
flowId: activeFlowId,
targetId,
},
})
: null;
@@ -13089,12 +13375,16 @@ function updatePanelModeUI() {
if (typeof updatePhoneVerificationSettingsUI === 'function') {
updatePhoneVerificationSettingsUI();
}
const panelMode = capabilityState?.effectivePanelMode || capabilityState?.panelMode || rawPanelMode;
const displayTargetId = normalizePanelMode(
activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? (capabilityState?.effectiveTargetId || targetId)
: getSelectedPanelMode(latestState)
);
const useCodex2Api = panelMode === 'codex2api';
const useCodex2Api = displayTargetId === 'codex2api';
const step9Btn = document.querySelector('.step-btn[data-step-key="platform-verify"]');
if (step9Btn && activeFlowId === DEFAULT_ACTIVE_FLOW_ID) {
step9Btn.textContent = panelMode === 'sub2api'
step9Btn.textContent = displayTargetId === 'sub2api'
? 'SUB2API 回调验证'
: (useCodex2Api ? 'Codex2API 回调验证' : 'CPA 回调验证');
}
@@ -14042,7 +14332,7 @@ const accountContributionManager = window.SidepanelContributionMode?.createContr
: String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID),
getSelectedTargetId: (flowId, state = latestState) => (typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(state, flowId)
: (String(state?.panelMode || state?.kiroTargetId || 'cpa').trim().toLowerCase() || 'cpa')),
: normalizeTargetIdForFlow(flowId, state?.targetId || '', getDefaultTargetIdForFlow(flowId))),
isModeSwitchBlocked: isContributionModeSwitchBlocked,
openConfirmModal,
openExternalUrl,
@@ -14570,7 +14860,9 @@ async function startAutoRunFromCurrentSettings() {
}
const validationState = {
...(latestState || {}),
panelMode: typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode,
targetId: typeof getSelectedTargetId === 'function'
? getSelectedTargetId(getSelectedFlowId(latestState))
: latestState?.targetId,
signupMethod: typeof getSelectedSignupMethod === 'function' ? getSelectedSignupMethod() : latestState?.signupMethod,
phoneVerificationEnabled: typeof inputPhoneVerificationEnabled !== 'undefined' && inputPhoneVerificationEnabled
? Boolean(inputPhoneVerificationEnabled.checked)
@@ -14582,7 +14874,7 @@ async function startAutoRunFromCurrentSettings() {
};
return registry.validateAutoRunStart({
activeFlowId: validationState.activeFlowId,
panelMode: validationState.panelMode,
targetId: validationState.targetId,
signupMethod: validationState.signupMethod,
state: validationState,
});
@@ -14650,11 +14942,7 @@ async function startAutoRunFromCurrentSettings() {
: (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
const targetId = typeof getSelectedTargetId === 'function'
? getSelectedTargetId(activeFlowId)
: (
activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? normalizePanelMode(latestState?.panelMode || 'cpa')
: (String(latestState?.kiroTargetId || 'kiro-rs').trim().toLowerCase() || 'kiro-rs')
);
: normalizeTargetIdForFlow(activeFlowId, latestState?.targetId || '', getDefaultTargetIdForFlow(activeFlowId));
inputAutoDelayMinutes.value = String(delayMinutes);
btnAutoRun.innerHTML = delayEnabled
? '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg> 计划中...'
@@ -15230,9 +15518,7 @@ selectPanelMode.addEventListener('change', async () => {
: (activeFlowId === DEFAULT_ACTIVE_FLOW_ID ? 'cpa' : 'kiro-rs');
const previousTargetId = typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(latestState, activeFlowId)
: (activeFlowId === DEFAULT_ACTIVE_FLOW_ID
? normalizePanelMode(latestState?.panelMode || defaultTargetId)
: String(latestState?.kiroTargetId || defaultTargetId).trim().toLowerCase() || defaultTargetId);
: normalizeTargetIdForFlow(activeFlowId, latestState?.targetId || '', defaultTargetId);
let nextTargetId = typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow(activeFlowId, selectPanelMode.value, defaultTargetId)
: (activeFlowId === DEFAULT_ACTIVE_FLOW_ID
@@ -15243,7 +15529,7 @@ selectPanelMode.addEventListener('change', async () => {
selectPanelMode.value = nextPanelMode;
const confirmed = await confirmCpaPhoneSignupIfNeeded({
signupMethod: getSelectedSignupMethod(),
panelMode: nextPanelMode,
targetId: nextPanelMode,
});
if (!confirmed) {
selectPanelMode.value = previousTargetId;
@@ -15254,7 +15540,7 @@ selectPanelMode.addEventListener('change', async () => {
syncLatestState({
activeFlowId,
flowId: activeFlowId,
panelMode: nextPanelMode,
targetId: nextPanelMode,
});
if (
typeof selectPlusAccountAccessStrategy !== 'undefined'
@@ -15278,7 +15564,7 @@ selectPanelMode.addEventListener('change', async () => {
syncLatestState({
activeFlowId,
flowId: activeFlowId,
kiroTargetId: nextTargetId,
targetId: nextTargetId,
});
}
updatePanelModeUI();
@@ -15305,7 +15591,7 @@ selectPlusAccountAccessStrategy?.addEventListener('change', () => {
selectPlusAccountAccessStrategy.value = nextUiValue;
selectPlusAccountAccessStrategy.dataset.requestedValue = resolvePlusAccountAccessStrategyForTarget(
nextUiValue,
typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode() : latestState?.panelMode
typeof getSelectedPanelMode === 'function' ? getSelectedPanelMode(latestState) : latestState?.targetId
);
updatePlusModeUI();
markSettingsDirty(true);
@@ -16100,19 +16386,13 @@ selectFlow?.addEventListener('change', () => {
: (nextActiveFlowId === DEFAULT_ACTIVE_FLOW_ID ? 'cpa' : 'kiro-rs');
const nextTargetId = typeof getSelectedTargetIdForState === 'function'
? getSelectedTargetIdForState(nextStateBase, nextActiveFlowId)
: (nextActiveFlowId === DEFAULT_ACTIVE_FLOW_ID
? normalizePanelMode(nextStateBase?.panelMode || defaultTargetId)
: String(nextStateBase?.kiroTargetId || defaultTargetId).trim().toLowerCase() || defaultTargetId);
: normalizeTargetIdForFlow(nextActiveFlowId, nextStateBase?.targetId || '', defaultTargetId);
syncLatestState({
activeFlowId: nextActiveFlowId,
flowId: nextActiveFlowId,
...(nextActiveFlowId === DEFAULT_ACTIVE_FLOW_ID
? { panelMode: normalizePanelMode(nextTargetId || defaultTargetId) }
: {
kiroTargetId: typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow(nextActiveFlowId, nextTargetId, defaultTargetId)
: (String(nextTargetId || defaultTargetId).trim().toLowerCase() || defaultTargetId),
}),
targetId: typeof normalizeTargetIdForFlow === 'function'
? normalizeTargetIdForFlow(nextActiveFlowId, nextTargetId, defaultTargetId)
: (String(nextTargetId || defaultTargetId).trim().toLowerCase() || defaultTargetId),
});
updatePanelModeUI();
if (typeof syncStepDefinitionsFromUiState === 'function') {
@@ -16320,7 +16600,7 @@ signupMethodButtons.forEach((button) => {
const nextSignupMethod = normalizeSignupMethod(button.dataset.signupMethod);
const confirmed = await confirmCpaPhoneSignupIfNeeded({
signupMethod: nextSignupMethod,
panelMode: getSelectedPanelMode(),
targetId: getSelectedPanelMode(latestState),
});
if (!confirmed) {
updateSignupMethodUI();
@@ -17047,15 +17327,18 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
setLocalCpaStep9Mode(message.payload.localCpaStep9Mode);
}
if (
message.payload.panelMode !== undefined
message.payload.targetId !== undefined
|| message.payload.activeFlowId !== undefined
|| message.payload.flowId !== undefined
|| message.payload.kiroTargetId !== undefined
) {
if (typeof syncFlowSelectorsFromState === 'function') {
syncFlowSelectorsFromState(latestState);
} else if (message.payload.panelMode !== undefined) {
selectPanelMode.value = normalizePanelMode(message.payload.panelMode || 'cpa');
} else if (message.payload.targetId !== undefined) {
selectPanelMode.value = normalizeTargetIdForFlow(
latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID,
message.payload.targetId || '',
getDefaultTargetIdForFlow(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID)
);
}
updatePanelModeUI();
}
+1 -1
View File
@@ -2,7 +2,7 @@ const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('content/signup-page.js', 'utf8');
const source = fs.readFileSync('flows/openai/content/openai-auth.js', 'utf8');
function extractFunction(name) {
const markers = [`async function ${name}(`, `function ${name}(`];
+2 -2
View File
@@ -2,8 +2,8 @@ const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const { createAuthPageRecovery } = require('../content/auth-page-recovery.js');
const source = fs.readFileSync('content/auth-page-recovery.js', 'utf8');
const { createAuthPageRecovery } = require('../flows/openai/content/auth-page-recovery.js');
const source = fs.readFileSync('flows/openai/content/auth-page-recovery.js', 'utf8');
function extractFunction(sourceText, name) {
const start = sourceText.indexOf(`function ${name}(`);
+2 -2
View File
@@ -311,10 +311,10 @@ async function runAutoSequenceFromStep() {
10: 'completed',
}),
tabRegistry: {
'signup-page': { tabId: 88, ready: true },
'openai-auth': { tabId: 88, ready: true },
},
sourceLastUrls: {
'signup-page': 'https://auth.openai.com/authorize',
'openai-auth': 'https://auth.openai.com/authorize',
},
};
}
@@ -285,7 +285,7 @@ return {
assert.equal(api.normalizePersistentSettingValue('signupMethod', 'unknown'), 'email');
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'codex'), 'openai');
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'kiro'), 'kiro');
assert.equal(api.normalizePersistentSettingValue('kiroTargetId', 'unknown'), 'kiro-rs');
assert.equal(api.normalizePersistentSettingValue('targetId', 'sub2api'), 'sub2api');
assert.equal(api.normalizePersistentSettingValue('kiroRsUrl', ''), '');
assert.equal(api.normalizePersistentSettingValue('kiroRsKey', ' key-1 '), 'key-1');
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', '5SIM'), '5sim');
+10 -10
View File
@@ -120,7 +120,7 @@ const self = {
},
},
};
const DEFAULT_STATE = { panelMode: 'cpa' };
const DEFAULT_STATE = { targetId: 'cpa' };
const CONTRIBUTION_RUNTIME_DEFAULTS = {
accountContributionEnabled: false,
accountContributionExpected: false,
@@ -150,7 +150,7 @@ return { buildAccountContributionState };
`)();
const enabledState = api.buildAccountContributionState(true, {
panelMode: 'sub2api',
targetId: 'sub2api',
customPassword: 'Secret123!',
accountRunHistoryTextEnabled: true,
}, {
@@ -166,12 +166,12 @@ return { buildAccountContributionState };
openai: { enabled: true, adapterId: 'openai-oauth' },
});
assert.equal(enabledState.contributionSessionId, 'session-001');
assert.equal(enabledState.panelMode, 'sub2api');
assert.equal(enabledState.targetId, 'sub2api');
assert.equal(enabledState.customPassword, '');
assert.equal(enabledState.accountRunHistoryTextEnabled, false);
const disabledState = api.buildAccountContributionState(false, {
panelMode: 'sub2api',
targetId: 'sub2api',
customPassword: 'Secret123!',
accountRunHistoryTextEnabled: true,
}, {
@@ -184,17 +184,17 @@ return { buildAccountContributionState };
assert.equal(disabledState.contributionAdapterId, '');
assert.deepStrictEqual(disabledState.flowContributionRuntime, {});
assert.equal(disabledState.contributionSessionId, '');
assert.equal(disabledState.panelMode, 'sub2api');
assert.equal(disabledState.targetId, 'sub2api');
assert.equal(disabledState.customPassword, 'Secret123!');
const plusContributionState = api.buildAccountContributionState(true, {
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: true,
customPassword: 'Secret123!',
accountRunHistoryTextEnabled: true,
}, {});
assert.equal(plusContributionState.contributionTargetGroupName, 'openai-plus');
assert.equal(plusContributionState.panelMode, 'sub2api');
assert.equal(plusContributionState.targetId, 'sub2api');
});
test('resetState preserves contribution runtime across reset', () => {
@@ -289,7 +289,7 @@ test('message router handles contribution mode, start flow, and status polling m
calls.push({ type: 'toggle', enabled });
return {
accountContributionEnabled: Boolean(enabled),
panelMode: 'cpa',
targetId: 'cpa',
};
},
startFlowContribution: async (options) => {
@@ -383,7 +383,7 @@ test('message router blocks AUTO_RUN and SCHEDULE_AUTO_RUN when shared auto-run
getPendingAutoRunTimerPlan: () => null,
getState: async () => ({
activeFlowId: 'site-a',
panelMode: 'cpa',
targetId: 'cpa',
signupMethod: 'phone',
stepStatuses: {},
}),
@@ -947,7 +947,7 @@ return { refreshOAuthUrlBeforeStep6 };
const oauthUrl = await api.refreshOAuthUrlBeforeStep6({
accountContributionEnabled: false,
panelMode: 'sub2api',
targetId: 'sub2api',
email: 'user@example.com',
});
+2 -2
View File
@@ -3,7 +3,7 @@ const fs = require('node:fs');
const test = require('node:test');
function loadCpaSessionImportModule() {
const source = fs.readFileSync('background/steps/cpa-session-import.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/cpa-session-import.js', 'utf8');
return new Function('self', `${source}; return self.MultiPageBackgroundCpaSessionImport;`)({});
}
@@ -78,7 +78,7 @@ test('CPA session import step reads current ChatGPT session and completes node',
assert.deepStrictEqual(ensureCalls[0].options.inject, [
'content/utils.js',
'content/operation-delay.js',
'content/plus-checkout.js',
'flows/openai/content/plus-checkout.js',
]);
assert.deepStrictEqual(sentMessages, [{
tabId: 91,
@@ -120,20 +120,20 @@ function isPlusModeState(state = {}) {
return Boolean(state?.plusModeEnabled);
}
function resolveCurrentFlowCapabilities(state = {}, options = {}) {
const normalizedPanelMode = String(options.panelMode || '').trim().toLowerCase();
const normalizedTargetId = String(options.targetId || '').trim().toLowerCase();
const requestedStrategy = normalizePlusAccountAccessStrategy(state.plusAccountAccessStrategy);
const effectiveStrategy = normalizedPanelMode === 'sub2api'
const effectiveStrategy = normalizedTargetId === 'sub2api'
? (requestedStrategy === 'sub2api_codex_session' ? 'sub2api_codex_session' : 'oauth')
: (normalizedPanelMode === 'cpa'
: (normalizedTargetId === 'cpa'
? (requestedStrategy === 'cpa_codex_session' ? 'cpa_codex_session' : 'oauth')
: 'oauth');
return {
effectivePanelMode: options.panelMode,
effectiveTargetId: options.targetId,
effectivePlusAccountAccessStrategy: effectiveStrategy,
effectiveSignupMethod: 'email',
stepDefinitionOptions: {
activeFlowId: 'openai',
panelMode: options.panelMode,
targetId: options.targetId,
plusModeEnabled: Boolean(state.plusModeEnabled),
plusPaymentMethod: normalizePlusPaymentMethod(state.plusPaymentMethod),
plusAccountAccessStrategy: effectiveStrategy,
@@ -158,7 +158,7 @@ test('background step resolution keeps SUB2API session tail only when the effect
const state = {
activeFlowId: 'openai',
flowId: 'openai',
panelMode: 'sub2api',
targetId: 'sub2api',
plusModeEnabled: true,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'sub2api_codex_session',
@@ -184,7 +184,7 @@ test('background step resolution keeps CPA session tail when the effective Plus
const state = {
activeFlowId: 'openai',
flowId: 'openai',
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: true,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'cpa_codex_session',
@@ -210,7 +210,7 @@ test('background step resolution falls back to OAuth tail when the requested ses
const state = {
activeFlowId: 'openai',
flowId: 'openai',
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: true,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'sub2api_codex_session',
@@ -3,34 +3,42 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
function loadDesktopAuthorizeRunnerApi() {
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
const clientSource = fs.readFileSync('background/kiro/desktop-client.js', 'utf8');
const runnerSource = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8');
const stateSource = fs.readFileSync('flows/kiro/background/state.js', 'utf8');
const clientSource = fs.readFileSync('flows/kiro/background/desktop-client.js', 'utf8');
const runnerSource = fs.readFileSync('flows/kiro/background/desktop-authorize-runner.js', 'utf8');
const globalScope = {};
new Function('self', `${stateSource}; ${clientSource}; ${runnerSource}; return self;`)(globalScope);
return globalScope.MultiPageBackgroundKiroDesktopAuthorizeRunner;
}
function getKiroRuntime(state = {}) {
return state?.runtimeState?.flowState?.kiro || {};
}
function createDesktopAuthorizeState(overrides = {}) {
return {
kiroRuntime: {
session: {
desktopTabId: 91,
runtimeState: {
flowState: {
kiro: {
session: {
desktopTabId: 91,
},
register: {
email: 'kiro-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'desktop-client-id',
clientSecret: 'desktop-client-secret',
state: 'desktop-state-001',
codeVerifier: 'desktop-code-verifier',
redirectUri: 'http://127.0.0.1:43121/oauth/callback',
redirectPort: 43121,
authorizeUrl: 'https://example.com/authorize',
},
upload: {},
},
},
register: {
email: 'kiro-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'desktop-client-id',
clientSecret: 'desktop-client-secret',
state: 'desktop-state-001',
codeVerifier: 'desktop-code-verifier',
redirectUri: 'http://127.0.0.1:43121/oauth/callback',
redirectPort: 43121,
authorizeUrl: 'https://example.com/authorize',
},
upload: {},
},
...overrides,
};
@@ -73,7 +81,7 @@ test('parseDesktopCallbackUrl validates state and redirect port', () => {
});
test('kiro desktop authorize runner uses a shared 3-minute page-load timeout budget', () => {
const source = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/desktop-authorize-runner.js', 'utf8');
assert.match(source, /DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS/);
assert.match(source, /createTimeoutBudget/);
assert.match(source, /resolveTimeoutBudget/);
@@ -95,14 +103,14 @@ test('background wires the Kiro register injector into desktop authorization run
});
test('kiro desktop authorization opens account page when no web session tab is tracked', () => {
const source = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/desktop-authorize-runner.js', 'utf8');
assert.match(source, /KIRO_WEB_ACCOUNT_URL = 'https:\/\/app\.kiro\.dev\/settings\/account'/);
assert.match(source, /chrome\.tabs\.create\(\{\s*url: KIRO_WEB_ACCOUNT_URL,\s*active: true,/);
assert.match(source, /未能从已打开页面确认 Kiro Web 登录态,正在打开 Kiro 账号页重新确认/);
});
test('kiro desktop authorization is gated by completed Kiro Web sign-in', () => {
const source = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/desktop-authorize-runner.js', 'utf8');
assert.match(source, /restoreKiroWebSessionFromOpenTabs/);
assert.match(source, /GET_KIRO_REGISTER_PAGE_STATE/);
assert.match(source, /Kiro Web 登录态尚未建立/);
@@ -112,12 +120,16 @@ test('kiro desktop authorization is gated by completed Kiro Web sign-in', () =>
test('executeKiroStartDesktopAuthorize restores existing Kiro Web session before desktop auth', async () => {
const api = loadDesktopAuthorizeRunnerApi();
let currentState = {
kiroRuntime: {
session: {},
register: {},
webAuth: {},
desktopAuth: {},
upload: {},
runtimeState: {
flowState: {
kiro: {
session: {},
register: {},
webAuth: {},
desktopAuth: {},
upload: {},
},
},
},
};
const setStateCalls = [];
@@ -178,7 +190,7 @@ test('executeKiroStartDesktopAuthorize restores existing Kiro Web session before
getState: async () => currentState,
getTabId: async () => null,
isTabAlive: async () => false,
KIRO_REGISTER_INJECT_FILES: ['content/kiro/register-page.js'],
KIRO_REGISTER_INJECT_FILES: ['flows/kiro/content/register-page.js'],
registerTab: async (source, tabId) => {
if (source === 'kiro-desktop-authorize') {
desktopRegisteredTabId = tabId;
@@ -213,10 +225,10 @@ test('executeKiroStartDesktopAuthorize restores existing Kiro Web session before
assert.equal(desktopRegisteredTabId, 88);
assert.match(openedAuthorizeUrl, /client_id=restored-client-id/);
assert.equal(currentState.email, 'restored@duck.com');
assert.equal(currentState.kiroRuntime.register.email, 'restored@duck.com');
assert.equal(currentState.kiroRuntime.register.status, 'completed');
assert.equal(currentState.kiroRuntime.webAuth.status, 'signed_in');
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.clientId, 'restored-client-id');
assert.equal(getKiroRuntime(currentState).register.email, 'restored@duck.com');
assert.equal(getKiroRuntime(currentState).register.status, 'completed');
assert.equal(getKiroRuntime(currentState).webAuth.status, 'signed_in');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.clientId, 'restored-client-id');
assert.equal(
logs.some(({ message }) => message.includes('检测到已有 Kiro Web 登录态,已恢复账号 restored@duck.com')),
true
@@ -227,12 +239,16 @@ test('executeKiroStartDesktopAuthorize restores existing Kiro Web session before
test('executeKiroStartDesktopAuthorize opens Kiro account page to restore login state', async () => {
const api = loadDesktopAuthorizeRunnerApi();
let currentState = {
kiroRuntime: {
session: {},
register: {},
webAuth: {},
desktopAuth: {},
upload: {},
runtimeState: {
flowState: {
kiro: {
session: {},
register: {},
webAuth: {},
desktopAuth: {},
upload: {},
},
},
},
};
const logs = [];
@@ -278,7 +294,7 @@ test('executeKiroStartDesktopAuthorize opens Kiro account page to restore login
ensureContentScriptReadyOnTab: async (source, tabId, options = {}) => {
assert.equal(source, 'kiro-register-page');
assert.equal(tabId, 91);
assert.deepEqual(options.inject, ['content/kiro/register-page.js']);
assert.deepEqual(options.inject, ['flows/kiro/content/register-page.js']);
assert.equal(options.injectSource, 'kiro-register-page');
},
fetchImpl: async () => ({
@@ -291,7 +307,7 @@ test('executeKiroStartDesktopAuthorize opens Kiro account page to restore login
getState: async () => currentState,
getTabId: async () => null,
isTabAlive: async () => false,
KIRO_REGISTER_INJECT_FILES: ['content/kiro/register-page.js'],
KIRO_REGISTER_INJECT_FILES: ['flows/kiro/content/register-page.js'],
registerTab: async (source, tabId) => {
registeredTabs.push({ source, tabId });
},
@@ -320,8 +336,8 @@ test('executeKiroStartDesktopAuthorize opens Kiro account page to restore login
await runner.executeKiroStartDesktopAuthorize(currentState);
assert.equal(createdTabUrl, 'https://app.kiro.dev/settings/account');
assert.equal(currentState.kiroRuntime.register.email, 'opened@duck.com');
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.clientId, 'opened-client-id');
assert.equal(getKiroRuntime(currentState).register.email, 'opened@duck.com');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.clientId, 'opened-client-id');
assert.deepEqual(registeredTabs, [
{ source: 'kiro-register-page', tabId: 91 },
{ source: 'kiro-desktop-authorize', tabId: 92 },
@@ -391,31 +407,35 @@ test('executeKiroCompleteDesktopAuthorize finishes from callback page without wa
await runner.executeKiroCompleteDesktopAuthorize(currentState);
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.authorizationCode, 'auth-code-001');
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.refreshToken, 'refresh-token-001');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.authorizationCode, 'auth-code-001');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.refreshToken, 'refresh-token-001');
});
test('executeKiroCompleteDesktopAuthorize waits for callback after consent even if original tab disappears', async () => {
const api = loadDesktopAuthorizeRunnerApi();
let currentState = createDesktopAuthorizeState({
kiroRuntime: {
session: {
desktopTabId: 91,
runtimeState: {
flowState: {
kiro: {
session: {
desktopTabId: 91,
},
register: {
email: 'kiro-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'desktop-client-id',
clientSecret: 'desktop-client-secret',
state: 'desktop-state-002',
codeVerifier: 'desktop-code-verifier',
redirectUri: 'http://127.0.0.1:43121/oauth/callback',
redirectPort: 43121,
authorizeUrl: 'https://example.com/authorize',
},
upload: {},
},
},
register: {
email: 'kiro-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'desktop-client-id',
clientSecret: 'desktop-client-secret',
state: 'desktop-state-002',
codeVerifier: 'desktop-code-verifier',
redirectUri: 'http://127.0.0.1:43121/oauth/callback',
redirectPort: 43121,
authorizeUrl: 'https://example.com/authorize',
},
upload: {},
},
});
let completedPayload = null;
@@ -505,6 +525,6 @@ test('executeKiroCompleteDesktopAuthorize waits for callback after consent even
await runner.executeKiroCompleteDesktopAuthorize(currentState);
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.authorizationCode, 'auth-code-002');
assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.refreshToken, 'refresh-token-002');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.authorizationCode, 'auth-code-002');
assert.equal(getKiroRuntime(completedPayload).desktopAuth?.refreshToken, 'refresh-token-002');
});
@@ -3,13 +3,17 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
function loadPublisherApi() {
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
const publisherSource = fs.readFileSync('background/kiro/publisher-kiro-rs.js', 'utf8');
const stateSource = fs.readFileSync('flows/kiro/background/state.js', 'utf8');
const publisherSource = fs.readFileSync('flows/kiro/background/publisher-kiro-rs.js', 'utf8');
const globalScope = {};
new Function('self', `${stateSource}; ${publisherSource}; return self;`)(globalScope);
return globalScope.MultiPageBackgroundKiroPublisherKiroRs;
}
function getKiroRuntime(state = {}) {
return state?.runtimeState?.flowState?.kiro || {};
}
test('kiro publisher exposes a factory and upload payload helpers', () => {
const api = loadPublisherApi();
assert.equal(typeof api?.createKiroRsPublisher, 'function');
@@ -20,7 +24,7 @@ test('kiro publisher exposes a factory and upload payload helpers', () => {
test('kiro publisher builds kiro.rs payload from desktop auth runtime with BuilderId profileArn', async () => {
const api = loadPublisherApi();
const payload = api.buildKiroRsPayload({
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
kiroRsUrl: 'https://kiro.example.com/admin',
kiroRsKey: 'demo-key',
ipProxyEnabled: true,
@@ -29,18 +33,22 @@ test('kiro publisher builds kiro.rs payload from desktop auth runtime with Build
ipProxyProtocol: 'http',
ipProxyUsername: 'proxy-user',
ipProxyPassword: 'proxy-pass',
kiroRuntime: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
runtimeState: {
flowState: {
kiro: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
},
},
},
},
});
@@ -69,22 +77,26 @@ test('kiro publisher reads latest kiro.rs key from background state instead of s
const api = loadPublisherApi();
const requests = [];
let liveState = {
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
kiroRsUrl: 'https://kiro.example.com/admin',
kiroRsKey: 'live-key',
email: 'aws-user@example.com',
kiroRuntime: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
runtimeState: {
flowState: {
kiro: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
},
},
},
},
settingsState: {
@@ -180,19 +192,23 @@ test('kiro publisher routes step 9 through public contribution upload when contr
flowId: 'kiro',
accountContributionEnabled: true,
contributionAdapterId: 'kiro-builder-id',
kiroTargetId: 'kiro-rs',
kiroRuntime: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
targetId: 'kiro-rs',
runtimeState: {
flowState: {
kiro: {
register: {
email: 'aws-user@example.com',
},
desktopAuth: {
region: 'us-east-1',
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-token-001',
},
upload: {
targetId: 'kiro-rs',
},
},
},
},
};
@@ -224,10 +240,10 @@ test('kiro publisher routes step 9 through public contribution upload when contr
assert.equal(requests.length, 0);
assert.equal(completed.length, 1);
assert.equal(completed[0].nodeId, 'kiro-upload-credential');
assert.equal(completed[0].payload.kiroRuntime.upload.targetId, 'contribution');
assert.equal(completed[0].payload.kiroRuntime.upload.status, 'uploaded');
assert.equal(completed[0].payload.kiroRuntime.upload.credentialId, 'kiro-contribution-009');
assert.equal(completed[0].payload.kiroRuntime.upload.lastMessage, '贡献链路成功:kiro-step-9');
assert.equal(getKiroRuntime(completed[0].payload).upload.targetId, 'contribution');
assert.equal(getKiroRuntime(completed[0].payload).upload.status, 'uploaded');
assert.equal(getKiroRuntime(completed[0].payload).upload.credentialId, 'kiro-contribution-009');
assert.equal(getKiroRuntime(completed[0].payload).upload.lastMessage, '贡献链路成功:kiro-step-9');
});
test('kiro publisher trims api key and includes fallback Authorization header during connection check', async () => {
@@ -3,13 +3,17 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
function loadRegisterRunnerApi() {
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
const runnerSource = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const stateSource = fs.readFileSync('flows/kiro/background/state.js', 'utf8');
const runnerSource = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
const globalScope = {};
new Function('self', `${stateSource}; ${runnerSource}; return self;`)(globalScope);
return globalScope.MultiPageBackgroundKiroRegisterRunner;
}
function getKiroRuntime(state = {}) {
return state?.runtimeState?.flowState?.kiro || {};
}
test('kiro register runner module exposes a factory and Kiro official sign-in entry', () => {
const api = loadRegisterRunnerApi();
assert.equal(typeof api?.createKiroRegisterRunner, 'function');
@@ -17,7 +21,7 @@ test('kiro register runner module exposes a factory and Kiro official sign-in en
});
test('kiro register runner removed the old AWS device authorization bootstrap', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
assert.doesNotMatch(source, /startBuilderIdDeviceLogin/);
assert.doesNotMatch(source, /device_authorization/);
assert.doesNotMatch(source, /verificationUriComplete/);
@@ -25,7 +29,7 @@ test('kiro register runner removed the old AWS device authorization bootstrap',
});
test('kiro register runner uses a shared 3-minute page-load timeout budget', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
assert.match(source, /DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS/);
assert.match(source, /createTimeoutBudget/);
assert.match(source, /resolveTimeoutBudget/);
@@ -34,7 +38,7 @@ test('kiro register runner uses a shared 3-minute page-load timeout budget', ()
});
test('kiro register consent step treats Kiro Web signed-in page as completion', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
assert.match(source, /readKiroRegisterPageState\(tabId, \{/);
assert.match(source, /\['authorization_page', 'success_page', 'kiro_web_signed_in'\]\.includes\(landingResult\?\.state\)/);
assert.match(source, /landingResult\?\.state === 'authorization_page'/);
@@ -42,7 +46,7 @@ test('kiro register consent step treats Kiro Web signed-in page as completion',
});
test('kiro register runner uses registration-only page states instead of shared OpenAI names', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
assert.match(source, /KIRO_REGISTER_PAGE_STATES/);
assert.match(source, /'register_otp_page'/);
assert.match(source, /'create_password_page'/);
@@ -54,7 +58,7 @@ test('kiro register runner uses registration-only page states instead of shared
});
test('kiro register runner fails existing-account login branches during registration', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/register-runner.js', 'utf8');
assert.match(source, /KIRO_REGISTER_EXISTING_ACCOUNT_STATES/);
assert.match(source, /assertKiroRegistrationOnlyState\(landingResult, currentState, 2, resolvedEmail\)/);
assert.match(source, /邮箱.*已进入 AWS Builder ID 登录页/);
@@ -64,12 +68,16 @@ test('kiro register runner fails existing-account login branches during registra
test('kiro submit-email stops immediately when AWS routes the email to login', async () => {
const api = loadRegisterRunnerApi();
const currentState = {
kiroRuntime: {
session: {
registerTabId: 101,
},
register: {
loginUrl: 'https://app.kiro.dev/signin',
runtimeState: {
flowState: {
kiro: {
session: {
registerTabId: 101,
},
register: {
loginUrl: 'https://app.kiro.dev/signin',
},
},
},
},
};
@@ -119,18 +127,22 @@ test('kiro submit-email stops immediately when AWS routes the email to login', a
assert.equal(completed, false);
assert.equal(sentMessages.some((message) => message.type === 'EXECUTE_NODE'), true);
assert.equal(statePatches.some((patch) => /已进入 AWS Builder ID 登录页/.test(patch.kiroRuntime?.session?.lastError || '')), true);
assert.equal(statePatches.some((patch) => /已进入 AWS Builder ID 登录页/.test(getKiroRuntime(patch).session?.lastError || '')), true);
});
test('kiro submit-email can adopt an already-open registration OTP page without allocating a new mailbox', async () => {
const api = loadRegisterRunnerApi();
const currentState = {
kiroRuntime: {
session: {
registerTabId: 102,
},
register: {
loginUrl: 'https://app.kiro.dev/signin',
runtimeState: {
flowState: {
kiro: {
session: {
registerTabId: 102,
},
register: {
loginUrl: 'https://app.kiro.dev/signin',
},
},
},
},
};
@@ -163,8 +175,8 @@ test('kiro submit-email can adopt an already-open registration OTP page without
await runner.executeKiroSubmitEmail({ nodeId: 'kiro-submit-email', ...currentState });
assert.equal(completedPayload?.kiroRuntime?.register?.email, 'manual-user@duck.com');
assert.equal(completedPayload?.kiroRuntime?.register?.status, 'waiting_otp');
assert.equal(completedPayload?.kiroRuntime?.register?.verificationRequestedAt, 0);
assert.equal(getKiroRuntime(completedPayload).register?.email, 'manual-user@duck.com');
assert.equal(getKiroRuntime(completedPayload).register?.status, 'waiting_otp');
assert.equal(getKiroRuntime(completedPayload).register?.verificationRequestedAt, 0);
assert.equal(sentMessages.some((message) => message.type === 'EXECUTE_NODE'), false);
});
+109 -85
View File
@@ -3,109 +3,128 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
function loadKiroStateApi() {
const source = fs.readFileSync('background/kiro/state.js', 'utf8');
const source = fs.readFileSync('flows/kiro/background/state.js', 'utf8');
const globalScope = {};
return new Function('self', `${source}; return self.MultiPageBackgroundKiroState;`)(globalScope);
}
test('background imports kiro state module and routes Kiro runtime through dedicated helpers', () => {
function getKiroRuntime(state = {}) {
return state?.runtimeState?.flowState?.kiro || {};
}
test('background imports kiro state module for Kiro runtime projection and reset helpers', () => {
const source = fs.readFileSync('background.js', 'utf8');
assert.match(source, /background\/kiro\/state\.js/);
assert.match(source, /flows\/kiro\/background\/state\.js/);
assert.match(source, /const kiroStateHelpers = self\.MultiPageBackgroundKiroState/);
assert.match(source, /kiroStateHelpers\?\.buildStateView/);
assert.match(source, /kiroStateHelpers\?\.buildSessionStatePatch/);
assert.match(source, /kiroStateHelpers\?\.buildDownstreamResetPatch/);
assert.match(source, /kiroStateHelpers\?\.applyNodeCompletionPayload/);
assert.doesNotMatch(source, /migrateLegacyKiroRuntimeState/);
assert.equal(/kiroStateHelpers\?\.buildSessionStatePatch/.test(source), false);
assert.equal(/kiroRuntime:\s*kiroStateHelpers\?\.buildDefaultRuntimeState/.test(source), false);
});
test('kiro state module exposes canonical nested kiroRuntime view', () => {
test('kiro state module exposes canonical nested runtimeState view', () => {
const api = loadKiroStateApi();
const view = api.buildStateView({
kiroTargetId: 'kiro-rs',
kiroRuntime: {
session: {
currentStage: 'desktop-authorize',
registerTabId: 88,
pageState: 'name_entry',
pageUrl: 'https://view.awsapps.com/start',
},
register: {
email: 'aws-user@example.com',
fullName: 'Ada Lovelace',
loginUrl: 'https://app.kiro.dev/signin',
status: 'waiting_name',
},
webAuth: {
status: 'signin_started',
hasAccessToken: false,
hasSessionToken: false,
},
desktopAuth: {
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-001',
status: 'authorized',
},
upload: {
targetId: 'kiro-rs',
status: 'ready_to_upload',
credentialId: 321,
},
const runtimeState = api.buildRuntimeStatePatch({}, {
session: {
currentStage: 'desktop-authorize',
registerTabId: 88,
pageState: 'name_entry',
pageUrl: 'https://view.awsapps.com/start',
},
register: {
email: 'aws-user@example.com',
fullName: 'Ada Lovelace',
loginUrl: 'https://app.kiro.dev/signin',
status: 'waiting_name',
},
webAuth: {
status: 'signin_started',
hasAccessToken: false,
hasSessionToken: false,
},
desktopAuth: {
clientId: 'client-001',
clientSecret: 'secret-001',
refreshToken: 'refresh-001',
status: 'authorized',
},
upload: {
targetId: 'kiro-rs',
status: 'ready_to_upload',
credentialId: 321,
},
}).runtimeState;
const view = api.buildStateView({
targetId: 'kiro-rs',
runtimeState,
});
assert.equal(view.kiroTargetId, 'kiro-rs');
assert.equal(view.kiroRuntime.session.currentStage, 'desktop-authorize');
assert.equal(view.kiroRuntime.session.registerTabId, 88);
assert.equal(view.kiroRuntime.register.email, 'aws-user@example.com');
assert.equal(view.kiroRuntime.register.loginUrl, 'https://app.kiro.dev/signin');
assert.equal(view.kiroRuntime.webAuth.status, 'signin_started');
assert.equal(view.kiroRuntime.desktopAuth.clientId, 'client-001');
assert.equal(view.kiroRuntime.desktopAuth.refreshToken, 'refresh-001');
assert.equal(view.kiroRuntime.upload.status, 'ready_to_upload');
assert.equal(view.kiroRuntime.upload.credentialId, 321);
assert.equal(view.targetId, 'kiro-rs');
assert.equal(Object.prototype.hasOwnProperty.call(view, 'kiroRuntime'), false);
assert.equal(getKiroRuntime(view).session.currentStage, 'desktop-authorize');
assert.equal(getKiroRuntime(view).session.registerTabId, 88);
assert.equal(getKiroRuntime(view).register.email, 'aws-user@example.com');
assert.equal(getKiroRuntime(view).register.loginUrl, 'https://app.kiro.dev/signin');
assert.equal(getKiroRuntime(view).webAuth.status, 'signin_started');
assert.equal(getKiroRuntime(view).desktopAuth.clientId, 'client-001');
assert.equal(getKiroRuntime(view).desktopAuth.refreshToken, 'refresh-001');
assert.equal(getKiroRuntime(view).upload.status, 'ready_to_upload');
assert.equal(getKiroRuntime(view).upload.credentialId, 321);
assert.equal(view.runtimeState.flowState.kiro.session.currentStage, 'desktop-authorize');
assert.equal(view.runtimeState.flowState.kiro.register.email, 'aws-user@example.com');
assert.equal(view.runtimeState.flowState.kiro.upload.credentialId, 321);
});
test('kiro state session patch accepts canonical nested runtime updates', () => {
const api = loadKiroStateApi();
const patch = api.buildSessionStatePatch({
kiroRuntime: api.buildDefaultRuntimeState(),
runtimeState: api.buildRuntimeStatePatch({}, api.buildDefaultRuntimeState()).runtimeState,
}, {
kiroRuntime: {
session: {
currentStage: 'register',
pageState: 'register_otp_page',
pageUrl: 'https://signin.aws/register',
},
register: {
email: 'aws-user@example.com',
fullName: 'Ada Lovelace',
verificationRequestedAt: 1700000000000,
},
desktopAuth: {
status: 'waiting_callback',
},
upload: {
status: 'waiting_register',
runtimeState: {
flowState: {
kiro: {
session: {
currentStage: 'register',
pageState: 'register_otp_page',
pageUrl: 'https://signin.aws/register',
},
register: {
email: 'aws-user@example.com',
fullName: 'Ada Lovelace',
verificationRequestedAt: 1700000000000,
},
desktopAuth: {
status: 'waiting_callback',
},
upload: {
status: 'waiting_register',
},
},
},
},
});
assert.equal(patch.kiroRuntime.session.currentStage, 'register');
assert.equal(patch.kiroRuntime.session.pageState, 'register_otp_page');
assert.equal(patch.kiroRuntime.session.pageUrl, 'https://signin.aws/register');
assert.equal(patch.kiroRuntime.register.email, 'aws-user@example.com');
assert.equal(patch.kiroRuntime.register.fullName, 'Ada Lovelace');
assert.equal(patch.kiroRuntime.register.verificationRequestedAt, 1700000000000);
assert.equal(patch.kiroRuntime.desktopAuth.status, 'waiting_callback');
assert.equal(patch.kiroRuntime.upload.status, 'waiting_register');
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'kiroRuntime'), false);
assert.equal(getKiroRuntime(patch).session.currentStage, 'register');
assert.equal(getKiroRuntime(patch).session.pageState, 'register_otp_page');
assert.equal(getKiroRuntime(patch).session.pageUrl, 'https://signin.aws/register');
assert.equal(getKiroRuntime(patch).register.email, 'aws-user@example.com');
assert.equal(getKiroRuntime(patch).register.fullName, 'Ada Lovelace');
assert.equal(getKiroRuntime(patch).register.verificationRequestedAt, 1700000000000);
assert.equal(getKiroRuntime(patch).desktopAuth.status, 'waiting_callback');
assert.equal(getKiroRuntime(patch).upload.status, 'waiting_register');
assert.equal(patch.runtimeState.flowState.kiro.session.currentStage, 'register');
assert.equal(patch.runtimeState.flowState.kiro.register.email, 'aws-user@example.com');
assert.equal(patch.runtimeState.flowState.kiro.desktopAuth.status, 'waiting_callback');
});
test('kiro state reset helpers clear downstream runtime and fresh keep-state preserves only target selection', () => {
const api = loadKiroStateApi();
const currentState = {
kiroTargetId: 'kiro-rs',
kiroRuntime: {
targetId: 'kiro-rs',
runtimeState: api.buildRuntimeStatePatch({}, {
session: {
currentStage: 'upload',
registerTabId: 88,
@@ -126,21 +145,26 @@ test('kiro state reset helpers clear downstream runtime and fresh keep-state pre
status: 'uploaded',
credentialId: 321,
},
},
}).runtimeState,
};
const resetPatch = api.buildDownstreamResetPatch('kiro-submit-email', currentState);
assert.equal(resetPatch.kiroRuntime.session.currentStage, 'register');
assert.equal(resetPatch.kiroRuntime.register.email, '');
assert.equal(resetPatch.kiroRuntime.register.fullName, '');
assert.equal(resetPatch.kiroRuntime.desktopAuth.refreshToken, '');
assert.equal(resetPatch.kiroRuntime.upload.status, '');
assert.equal(resetPatch.kiroRuntime.upload.credentialId, null);
assert.equal(Object.prototype.hasOwnProperty.call(resetPatch, 'kiroRuntime'), false);
assert.equal(getKiroRuntime(resetPatch).session.currentStage, 'register');
assert.equal(getKiroRuntime(resetPatch).register.email, '');
assert.equal(getKiroRuntime(resetPatch).register.fullName, '');
assert.equal(getKiroRuntime(resetPatch).desktopAuth.refreshToken, '');
assert.equal(getKiroRuntime(resetPatch).upload.status, '');
assert.equal(getKiroRuntime(resetPatch).upload.credentialId, null);
assert.equal(resetPatch.runtimeState.flowState.kiro.register.email, '');
assert.equal(resetPatch.runtimeState.flowState.kiro.desktopAuth.refreshToken, '');
const keepState = api.buildFreshKeepState(currentState);
assert.equal(keepState.kiroTargetId, 'kiro-rs');
assert.equal(keepState.kiroRuntime.register.email, '');
assert.equal(keepState.kiroRuntime.desktopAuth.refreshToken, '');
assert.equal(keepState.kiroRuntime.upload.status, '');
assert.equal(keepState.kiroRuntime.upload.targetId, 'kiro-rs');
assert.equal(keepState.targetId, 'kiro-rs');
assert.equal(Object.prototype.hasOwnProperty.call(keepState, 'kiroRuntime'), false);
assert.equal(getKiroRuntime(keepState).register.email, '');
assert.equal(getKiroRuntime(keepState).desktopAuth.refreshToken, '');
assert.equal(getKiroRuntime(keepState).upload.status, '');
assert.equal(getKiroRuntime(keepState).upload.targetId, 'kiro-rs');
assert.equal(keepState.runtimeState.flowState.kiro.upload.targetId, 'kiro-rs');
});
@@ -4,11 +4,11 @@ const fs = require('node:fs');
test('background imports logging/status module', () => {
const source = fs.readFileSync('background.js', 'utf8');
assert.match(source, /background\/logging-status\.js/);
assert.match(source, /core\/flow-kernel\/logging-status\.js/);
});
test('logging/status module exposes a factory', () => {
const source = fs.readFileSync('background/logging-status.js', 'utf8');
const source = fs.readFileSync('core/flow-kernel/logging-status.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundLoggingStatus;`)(globalScope);
@@ -17,7 +17,7 @@ test('logging/status module exposes a factory', () => {
});
test('logging/status add-phone detection ignores step 2 phone-entry switch failures', () => {
const source = fs.readFileSync('background/logging-status.js', 'utf8');
const source = fs.readFileSync('core/flow-kernel/logging-status.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundLoggingStatus;`)(globalScope);
@@ -39,7 +39,7 @@ test('signup flow helper allocates mail2925 account before generating alias emai
calls.setEmail.push(email);
},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => null,
});
@@ -87,7 +87,7 @@ test('signup flow helper skips mail2925 account allocation when account pool swi
sendToContentScriptResilient: async () => ({}),
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => null,
});
+23 -23
View File
@@ -459,7 +459,7 @@ test('SAVE_SETTING rebuilds Plus node statuses when panel mode forces the effect
const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope);
const broadcasts = [];
let state = {
panelMode: 'sub2api',
targetId: 'sub2api',
plusModeEnabled: true,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'sub2api_codex_session',
@@ -486,12 +486,12 @@ test('SAVE_SETTING rebuilds Plus node statuses when panel mode forces the effect
const router = api.createMessageRouter({
addLog: async () => {},
buildLuckmailSessionSettingsPayload: () => ({}),
buildPersistentSettingsPayload: (input = {}) => Object.prototype.hasOwnProperty.call(input, 'panelMode')
? { panelMode: input.panelMode }
buildPersistentSettingsPayload: (input = {}) => Object.prototype.hasOwnProperty.call(input, 'targetId')
? { targetId: input.targetId }
: {},
broadcastDataUpdate: (payload) => broadcasts.push(payload),
getNodeIdsForState: (nextState = {}) => (
String(nextState.panelMode || '').trim() === 'sub2api'
String(nextState.targetId || '').trim() === 'sub2api'
&& String(nextState.plusAccountAccessStrategy || '').trim() === 'sub2api_codex_session'
? [
'open-chatgpt',
@@ -525,12 +525,12 @@ test('SAVE_SETTING rebuilds Plus node statuses when panel mode forces the effect
const response = await router.handleMessage({
type: 'SAVE_SETTING',
payload: {
panelMode: 'cpa',
targetId: 'cpa',
},
});
assert.equal(response.ok, true);
assert.equal(state.panelMode, 'cpa');
assert.equal(state.targetId, 'cpa');
assert.equal(state.plusAccountAccessStrategy, 'sub2api_codex_session');
assert.equal(state.currentNodeId, '');
assert.equal(state.oauthUrl, null);
@@ -552,7 +552,7 @@ test('SAVE_SETTING rebuilds Plus node statuses when panel mode forces the effect
});
assert.equal(Object.prototype.hasOwnProperty.call(state.nodeStatuses, 'sub2api-session-import'), false);
assert.deepStrictEqual(broadcasts.at(-1), {
panelMode: 'cpa',
targetId: 'cpa',
signupMethod: 'email',
oauthUrl: null,
localhostUrl: null,
@@ -595,7 +595,7 @@ test('SAVE_SETTING mirrors activeFlowId into flowId when switching to kiro flow'
const globalScope = { console };
const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope);
const broadcasts = [];
let state = { activeFlowId: 'openai', flowId: 'openai', panelMode: 'cpa', plusModeEnabled: false, plusPaymentMethod: 'paypal' };
let state = { activeFlowId: 'openai', flowId: 'openai', targetId: 'cpa', plusModeEnabled: false, plusPaymentMethod: 'paypal' };
const router = api.createMessageRouter({
addLog: async () => {},
@@ -683,7 +683,7 @@ test('SAVE_SETTING syncs canonical kiro settingsState back into session state',
let state = {
activeFlowId: 'kiro',
flowId: 'kiro',
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
kiroRsUrl: 'https://kiro.example.com/admin',
kiroRsKey: '',
settingsSchemaVersion: 4,
@@ -718,7 +718,7 @@ test('SAVE_SETTING syncs canonical kiro settingsState back into session state',
setPersistentSettings: async () => ({
activeFlowId: 'kiro',
flowId: 'kiro',
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
kiroRsUrl: 'https://kiro.example.com/admin',
kiroRsKey: 'live-key',
settingsSchemaVersion: 4,
@@ -751,7 +751,7 @@ test('CHECK_KIRO_RS_CONNECTION prefers current sidepanel payload over stale save
getState: async () => ({
activeFlowId: 'kiro',
flowId: 'kiro',
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
kiroRsUrl: 'https://old.example.com/admin',
kiroRsKey: 'old-key',
settingsState: {
@@ -808,7 +808,7 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
let state = {
activeFlowId: 'openai',
flowId: 'openai',
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: false,
plusPaymentMethod: 'paypal',
};
@@ -829,7 +829,7 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
validations.push({
activeFlowId: validationState?.activeFlowId,
flowId: validationState?.flowId,
kiroTargetId: validationState?.kiroTargetId,
targetId: validationState?.targetId,
optionActiveFlowId: options?.activeFlowId,
});
return { ok: true, errors: [] };
@@ -848,14 +848,14 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
assert.equal(response.ok, true);
assert.equal(state.activeFlowId, 'kiro');
assert.equal(state.flowId, 'kiro');
assert.equal(state.kiroTargetId, 'kiro-rs');
assert.equal(state.targetId, 'kiro-rs');
assert.deepStrictEqual(calls, [
{
type: 'setState',
updates: {
activeFlowId: 'kiro',
flowId: 'kiro',
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
},
},
{
@@ -877,7 +877,7 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
{
activeFlowId: 'kiro',
flowId: 'kiro',
kiroTargetId: 'kiro-rs',
targetId: 'kiro-rs',
optionActiveFlowId: 'kiro',
},
]);
@@ -891,18 +891,18 @@ test('SAVE_SETTING re-resolves signup method when panel mode changes', async ()
signupMethod: 'phone',
phoneVerificationEnabled: true,
plusModeEnabled: false,
panelMode: 'sub2api',
targetId: 'sub2api',
};
const router = api.createMessageRouter({
addLog: async () => {},
buildLuckmailSessionSettingsPayload: () => ({}),
buildPersistentSettingsPayload: (input = {}) => Object.prototype.hasOwnProperty.call(input, 'panelMode')
? { panelMode: input.panelMode }
buildPersistentSettingsPayload: (input = {}) => Object.prototype.hasOwnProperty.call(input, 'targetId')
? { targetId: input.targetId }
: {},
broadcastDataUpdate: () => {},
getState: async () => ({ ...state }),
resolveSignupMethod: (nextState = {}) => nextState.panelMode === 'cpa' ? 'email' : 'phone',
resolveSignupMethod: (nextState = {}) => nextState.targetId === 'cpa' ? 'email' : 'phone',
setPersistentSettings: async (updates) => ({ ...updates }),
setState: async (updates) => {
state = { ...state, ...updates };
@@ -911,11 +911,11 @@ test('SAVE_SETTING re-resolves signup method when panel mode changes', async ()
const response = await router.handleMessage({
type: 'SAVE_SETTING',
payload: { panelMode: 'cpa' },
payload: { targetId: 'cpa' },
});
assert.equal(response.ok, true);
assert.equal(state.panelMode, 'cpa');
assert.equal(state.targetId, 'cpa');
assert.equal(state.signupMethod, 'email');
});
@@ -929,7 +929,7 @@ test('SAVE_SETTING applies shared mode-switch normalization before persisting in
signupMethod: 'email',
phoneVerificationEnabled: false,
plusModeEnabled: false,
panelMode: 'cpa',
targetId: 'cpa',
};
const router = api.createMessageRouter({
@@ -426,7 +426,7 @@ test('message router finalizes step 3 before marking it completed', async () =>
const response = await router.handleMessage({
type: 'NODE_COMPLETE',
nodeId: 'fill-password',
source: 'signup-page',
source: 'openai-auth',
payload: {
nodeId: 'fill-password',
email: 'user@example.com',
@@ -548,7 +548,7 @@ test('message router marks step 3 failed when post-submit finalize fails', async
const response = await router.handleMessage({
type: 'NODE_COMPLETE',
nodeId: 'fill-password',
source: 'signup-page',
source: 'openai-auth',
payload: {
nodeId: 'fill-password',
email: 'user@example.com',
@@ -581,7 +581,7 @@ test('message router does not duplicate step 3 mismatch failure log after finali
const response = await router.handleMessage({
type: 'NODE_ERROR',
nodeId: 'fill-password',
source: 'signup-page',
source: 'openai-auth',
payload: { nodeId: 'fill-password' },
error: mismatchError,
}, {});
@@ -604,7 +604,7 @@ test('message router stops the flow and surfaces cloudflare security block error
const response = await router.handleMessage({
type: 'NODE_ERROR',
nodeId: 'oauth-login',
source: 'signup-page',
source: 'openai-auth',
payload: { nodeId: 'oauth-login' },
error: 'CF_SECURITY_BLOCKED::您已触发Cloudflare 安全防护系统',
}, {});
@@ -68,7 +68,7 @@ test('navigation utils support codex2api mode and url normalization', () => {
utils.normalizeCodex2ApiUrl('https://codex-admin.example.com/'),
'https://codex-admin.example.com/admin/accounts'
);
assert.equal(utils.getPanelMode({ panelMode: 'codex2api' }), 'codex2api');
assert.equal(utils.getPanelMode({ targetId: 'codex2api' }), 'codex2api');
assert.equal(utils.getPanelModeLabel('codex2api'), 'Codex2API');
});
@@ -4,10 +4,10 @@ const fs = require('node:fs');
test('confirm-oauth and platform-verify stay free of operation delay gate calls', () => {
for (const file of [
'background/steps/confirm-oauth.js',
'background/steps/platform-verify.js',
'flows/openai/background/steps/confirm-oauth.js',
'flows/openai/background/steps/platform-verify.js',
'background/panel-bridge.js',
'content/sub2api-panel.js',
'flows/openai/content/sub2api-panel.js',
]) {
const source = fs.readFileSync(file, 'utf8');
assert.doesNotMatch(source, /performOperationWithDelay\(/, `${file} must not call the operation delay gate`);
@@ -3,7 +3,7 @@ const fs = require('node:fs');
const test = require('node:test');
test('platform verify module supports codex2api protocol callback exchange', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, options = {}) => {
assert.equal(url, 'http://localhost:8080/api/admin/oauth/exchange-code');
@@ -81,7 +81,7 @@ test('platform verify module supports codex2api protocol callback exchange', asy
});
test('platform verify retries transient SUB2API oauth/token exchange errors before succeeding', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
const logs = [];
@@ -150,7 +150,7 @@ test('platform verify retries transient SUB2API oauth/token exchange errors befo
});
test('platform verify retries transient SUB2API token_exchange_user_error before succeeding', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
const logs = [];
@@ -44,7 +44,7 @@ function createDeps(overrides = {}) {
function loadStep10WithSub2Api() {
const sub2apiSource = fs.readFileSync('background/sub2api-api.js', 'utf8');
const step10Source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const step10Source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
return new Function('self', `${sub2apiSource}\n${step10Source}; return self.MultiPageBackgroundStep10;`)({});
}
@@ -57,7 +57,7 @@ function createSub2ApiResponse(payload, status = 200) {
}
test('platform verify module submits CPA callback via management API first', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
let uiCalled = false;
globalThis.fetch = async (url, options = {}) => {
@@ -114,7 +114,7 @@ test('platform verify module submits CPA callback via management API first', asy
});
test('platform verify module prefers cpaManagementOrigin when provided', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
globalThis.fetch = async (url, options = {}) => {
assert.equal(url, 'http://localhost:9999/v0/management/oauth-callback');
@@ -149,7 +149,7 @@ test('platform verify module prefers cpaManagementOrigin when provided', async (
});
test('platform verify module fails fast when CPA API submit fails', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => ({
ok: false,
@@ -185,7 +185,7 @@ test('platform verify module fails fast when CPA API submit fails', async () =>
});
test('platform verify module requires management key for CPA API-only flow', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
let fetchCalled = false;
globalThis.fetch = async () => {
@@ -221,7 +221,7 @@ test('platform verify module requires management key for CPA API-only flow', asy
});
test('platform verify module rejects callback when cpa oauth state mismatches', async () => {
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
const source = fs.readFileSync('flows/openai/background/steps/platform-verify.js', 'utf8');
const originalFetch = globalThis.fetch;
let fetchCalled = false;
globalThis.fetch = async () => {
+56 -6
View File
@@ -3,14 +3,14 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
function loadRuntimeStateApi() {
const source = fs.readFileSync('background/runtime-state.js', 'utf8');
const source = fs.readFileSync('core/flow-kernel/runtime-state.js', 'utf8');
const globalScope = {};
return new Function('self', `${source}; return self.MultiPageBackgroundRuntimeState;`)(globalScope);
}
test('background imports runtime-state module and wires state view helpers', () => {
const source = fs.readFileSync('background.js', 'utf8');
assert.match(source, /background\/runtime-state\.js/);
assert.match(source, /core\/flow-kernel\/runtime-state\.js/);
assert.match(source, /createRuntimeStateHelpers/);
assert.match(source, /buildStateViewWithRuntimeState/);
assert.match(source, /buildStatePatchWithRuntimeState/);
@@ -46,10 +46,10 @@ test('runtime-state view preserves canonical flow metadata from node state', ()
phoneNumber: '+447700900123',
},
tabRegistry: {
'signup-page': { tabId: 12 },
'openai-auth': { tabId: 12 },
},
sourceLastUrls: {
'signup-page': 'https://auth.example.com/start',
'openai-auth': 'https://auth.example.com/start',
},
flowStartTime: 12345,
});
@@ -72,10 +72,10 @@ test('runtime-state view preserves canonical flow metadata from node state', ()
});
assert.deepStrictEqual(view.sharedState, {
tabRegistry: {
'signup-page': { tabId: 12 },
'openai-auth': { tabId: 12 },
},
sourceLastUrls: {
'signup-page': 'https://auth.example.com/start',
'openai-auth': 'https://auth.example.com/start',
},
flowStartTime: 12345,
});
@@ -171,3 +171,53 @@ test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId
'kiro-open-register-page': 'running',
});
});
test('runtime-state patch preserves canonical nested kiro flow state without projecting a top-level alias', () => {
const api = loadRuntimeStateApi();
const helpers = api.createRuntimeStateHelpers({
DEFAULT_ACTIVE_FLOW_ID: 'openai',
defaultNodeStatuses: {
'open-chatgpt': 'pending',
'kiro-open-register-page': 'pending',
},
});
const patch = helpers.buildSessionStatePatch({
runtimeState: {
activeFlowId: 'kiro',
flowState: {
kiro: {
session: {
currentStage: 'register',
},
register: {
email: 'old-user@example.com',
},
},
},
},
}, {
runtimeState: {
flowState: {
kiro: {
register: {
email: 'aws-user@example.com',
},
upload: {
status: 'uploaded',
},
},
},
},
});
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'kiroRuntime'), false);
assert.equal(patch.runtimeState.flowState.kiro.session.currentStage, 'register');
assert.equal(patch.runtimeState.flowState.kiro.register.email, 'aws-user@example.com');
assert.equal(patch.runtimeState.flowState.kiro.upload.status, 'uploaded');
const view = helpers.buildStateView(patch);
assert.equal(Object.prototype.hasOwnProperty.call(view, 'kiroRuntime'), false);
assert.equal(view.flowState.kiro.register.email, 'aws-user@example.com');
assert.equal(view.flowState.kiro.upload.status, 'uploaded');
});
@@ -52,12 +52,24 @@ test('importSettingsBundle normalizes unsupported capability flags before persis
const api = new Function(`
const SETTINGS_EXPORT_SCHEMA_VERSION = 1;
const DEFAULT_REGISTRATION_EMAIL_STATE = { emailHistory: [] };
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const self = {
MultiPageLegacySettingsImporter: {
createSettingsImporter() {
return {
importSettings(settings = {}) {
return { ...settings };
},
};
},
},
};
let persistedUpdates = null;
let stateUpdates = null;
let broadcastPayload = null;
let currentState = {
activeFlowId: 'site-a',
panelMode: 'sub2api',
targetId: 'sub2api',
signupMethod: 'phone',
plusModeEnabled: false,
phoneVerificationEnabled: false,
@@ -74,7 +86,7 @@ function validateModeSwitchState() {
ok: false,
errors: [{ code: 'panel_mode_unsupported', message: '当前 flow 不支持 SUB2API 面板模式。' }],
normalizedUpdates: {
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: false,
phoneVerificationEnabled: false,
signupMethod: 'email',
@@ -84,6 +96,9 @@ function validateModeSwitchState() {
function resolveSignupMethod(state = {}) {
return String(state?.signupMethod || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email';
}
function getSettingsSchemaApi() {
return null;
}
async function setPersistentSettings(updates) {
persistedUpdates = { ...updates };
}
@@ -109,7 +124,7 @@ return {
const result = await api.importSettingsBundle({
schemaVersion: 1,
settings: {
panelMode: 'sub2api',
targetId: 'sub2api',
plusModeEnabled: true,
phoneVerificationEnabled: true,
signupMethod: 'phone',
@@ -117,16 +132,164 @@ return {
});
assert.deepEqual(api.getPersistedUpdates(), {
panelMode: 'cpa',
targetId: 'cpa',
plusModeEnabled: false,
phoneVerificationEnabled: false,
signupMethod: 'email',
});
assert.equal(api.getStateUpdates().panelMode, 'cpa');
assert.equal(api.getStateUpdates().targetId, 'cpa');
assert.equal(api.getStateUpdates().plusModeEnabled, false);
assert.equal(api.getStateUpdates().phoneVerificationEnabled, false);
assert.equal(api.getStateUpdates().signupMethod, 'email');
assert.equal(api.getBroadcastPayload().panelMode, 'cpa');
assert.equal(api.getBroadcastPayload().targetId, 'cpa');
assert.equal(api.getBroadcastPayload().signupMethod, 'email');
assert.equal(result.signupMethod, 'email');
});
test('importSettingsBundle routes legacy settings through the legacy importer before persisting', async () => {
const api = new Function(`
const SETTINGS_EXPORT_SCHEMA_VERSION = 1;
const DEFAULT_REGISTRATION_EMAIL_STATE = { emailHistory: [] };
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
let importerInput = null;
let persistedUpdates = null;
let currentState = {
activeFlowId: 'openai',
nodeStatuses: {},
};
const self = {
MultiPageFlowRegistry: {
DEFAULT_FLOW_ID: 'openai',
},
MultiPageLegacySettingsImporter: {
createSettingsImporter() {
return {
importSettings(settings = {}) {
importerInput = JSON.parse(JSON.stringify(settings));
return {
settingsSchemaVersion: 5,
settingsState: {
schemaVersion: 5,
activeFlowId: 'kiro',
services: {
account: { customPassword: '' },
email: { provider: '163' },
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
},
flows: {
openai: {
selectedTargetId: 'cpa',
targets: {
cpa: { vpsUrl: '', vpsPassword: '', localCpaStep9Mode: 'submit' },
sub2api: {
sub2apiUrl: '',
sub2apiEmail: '',
sub2apiPassword: '',
sub2apiGroupName: 'codex',
sub2apiGroupNames: ['codex', 'openai-plus'],
sub2apiAccountPriority: 1,
sub2apiDefaultProxyName: '',
},
codex2api: { codex2apiUrl: '', codex2apiAdminKey: '' },
},
signup: {
signupMethod: 'email',
phoneVerificationEnabled: false,
phoneSignupReloginAfterBindEmailEnabled: false,
},
plus: {
plusModeEnabled: false,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'oauth',
},
autoRun: {
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 11 },
},
},
kiro: {
selectedTargetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
apiKey: 'imported-key',
},
},
autoRun: {
stepExecutionRange: { enabled: false, fromStep: 1, toStep: 9 },
},
},
},
},
};
},
};
},
},
};
async function ensureManualInteractionAllowed() {
return currentState;
}
function buildPersistentSettingsPayload(settings = {}) {
return {
activeFlowId: settings.settingsState.activeFlowId,
targetId: 'cpa',
signupMethod: 'email',
targetId: 'kiro-rs',
kiroRsUrl: settings.settingsState.flows.kiro.targets['kiro-rs'].baseUrl,
kiroRsKey: settings.settingsState.flows.kiro.targets['kiro-rs'].apiKey,
settingsSchemaVersion: settings.settingsSchemaVersion,
settingsState: settings.settingsState,
};
}
function validateModeSwitchState() {
return { normalizedUpdates: {} };
}
function resolveSignupMethod() {
return 'email';
}
function getSettingsSchemaApi() {
return null;
}
async function setPersistentSettings(updates) {
persistedUpdates = { ...updates };
return updates;
}
async function setState(updates) {
currentState = { ...currentState, ...updates };
}
function broadcastDataUpdate() {}
async function getState() {
return currentState;
}
${extractFunction('importSettingsBundle')}
return {
importSettingsBundle,
getImporterInput: () => importerInput,
getPersistedUpdates: () => persistedUpdates,
};
`)();
await api.importSettingsBundle({
schemaVersion: 1,
settings: {
targetId: 'sub2api',
kiroRuntime: {
upload: {
status: 'uploaded',
},
},
},
});
assert.deepEqual(api.getImporterInput(), {
targetId: 'sub2api',
kiroRuntime: {
upload: {
status: 'uploaded',
},
},
});
assert.equal(api.getPersistedUpdates().activeFlowId, 'kiro');
assert.equal(api.getPersistedUpdates().settingsSchemaVersion, 5);
assert.equal(api.getPersistedUpdates().settingsState.flows.kiro.targets['kiro-rs'].apiKey, 'imported-key');
});
@@ -1,9 +1,10 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const { readFlowRegistryBundle } = require('./helpers/script-bundles.js');
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
const settingsSchemaSource = fs.readFileSync('shared/settings-schema.js', 'utf8');
const flowRegistrySource = readFlowRegistryBundle();
const settingsSchemaSource = fs.readFileSync('core/flow-kernel/settings-schema.js', 'utf8');
const backgroundSource = fs.readFileSync('background.js', 'utf8');
function extractFunction(name) {
@@ -59,9 +60,7 @@ const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const DEFAULT_SUB2API_GROUP_NAMES = ['codex', 'openai-plus'];
const SETTINGS_SCHEMA_VIEW_KEYS = Object.freeze([
'activeFlowId',
'openaiIntegrationTargetId',
'panelMode',
'kiroTargetId',
'targetId',
'vpsUrl',
'vpsPassword',
'localCpaStep9Mode',
@@ -92,7 +91,7 @@ const SETTINGS_SCHEMA_VIEW_KEYS = Object.freeze([
const SETTINGS_SCHEMA_VIEW_KEY_SET = new Set(SETTINGS_SCHEMA_VIEW_KEYS);
const PERSISTED_SETTING_DEFAULTS = {
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
panelMode: 'cpa',
targetId: 'cpa',
signupMethod: 'email',
plusModeEnabled: false,
plusPaymentMethod: 'paypal',
@@ -102,7 +101,6 @@ const PERSISTED_SETTING_DEFAULTS = {
ipProxyEnabled: false,
ipProxyService: '711proxy',
ipProxyMode: 'account',
kiroTargetId: 'kiro-rs',
kiroRsUrl: '',
kiroRsKey: '',
stepExecutionRangeByFlow: {},
@@ -203,13 +201,13 @@ test('buildPersistentSettingsPayload writes canonical settings schema into persi
}, { fillDefaults: true });
assert.equal(payload.activeFlowId, 'kiro');
assert.equal(payload.kiroTargetId, 'kiro-rs');
assert.equal(payload.targetId, 'kiro-rs');
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
assert.equal(payload.kiroRsKey, 'secret-key');
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
assert.equal(payload.settingsSchemaVersion, 4);
assert.equal(payload.settingsSchemaVersion, 5);
assert.equal(payload.settingsState.activeFlowId, 'kiro');
assert.equal(payload.settingsState.flows.kiro.targetId, 'kiro-rs');
assert.equal(payload.settingsState.flows.kiro.selectedTargetId, 'kiro-rs');
assert.equal(
payload.settingsState.flows.kiro.targets['kiro-rs'].baseUrl,
'https://kiro.example.com/admin'
@@ -220,7 +218,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
const api = buildHarness();
const payload = api.buildPersistentSettingsPayload({
settingsSchemaVersion: 4,
settingsSchemaVersion: 5,
settingsState: {
activeFlowId: 'kiro',
services: {
@@ -230,8 +228,8 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
},
flows: {
openai: {
integrationTargetId: 'cpa',
integrationTargets: {
selectedTargetId: 'cpa',
targets: {
cpa: {
vpsUrl: '',
vpsPassword: '',
@@ -266,7 +264,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
},
},
kiro: {
targetId: 'kiro-rs',
selectedTargetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
@@ -282,11 +280,11 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
}, { requireKnownKeys: true });
assert.equal(payload.activeFlowId, 'kiro');
assert.equal(payload.kiroTargetId, 'kiro-rs');
assert.equal(payload.targetId, 'kiro-rs');
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
assert.equal(payload.kiroRsKey, 'schema-only-key');
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
assert.equal(payload.settingsSchemaVersion, 4);
assert.equal(payload.settingsSchemaVersion, 5);
assert.equal(payload.settingsState.flows.openai.plus.plusAccountAccessStrategy, 'oauth');
});
@@ -313,7 +311,7 @@ function getRequestedKeys() {
assert.ok(api.getRequestedKeys().includes('settingsSchemaVersion'));
assert.ok(api.getRequestedKeys().includes('settingsState'));
assert.ok(api.getRequestedKeys().includes('plusAccountAccessStrategy'));
assert.equal(state.settingsSchemaVersion, 4);
assert.equal(state.settingsSchemaVersion, 5);
assert.equal(state.settingsState.activeFlowId, 'openai');
});
@@ -324,7 +322,7 @@ const chrome = {
local: {
async get() {
return {
settingsSchemaVersion: 4,
settingsSchemaVersion: 5,
settingsState: {
activeFlowId: 'kiro',
services: {
@@ -334,8 +332,8 @@ const chrome = {
},
flows: {
openai: {
integrationTargetId: 'sub2api',
integrationTargets: {
selectedTargetId: 'sub2api',
targets: {
cpa: {
vpsUrl: '',
vpsPassword: '',
@@ -370,7 +368,7 @@ const chrome = {
},
},
kiro: {
targetId: 'kiro-rs',
selectedTargetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
@@ -393,7 +391,8 @@ const chrome = {
const state = await api.getPersistedSettings();
assert.equal(state.activeFlowId, 'kiro');
assert.equal(state.panelMode, 'sub2api');
assert.equal(state.targetId, 'kiro-rs');
assert.equal(state.settingsState.flows.openai.selectedTargetId, 'sub2api');
assert.equal(state.mailProvider, 'hotmail');
assert.equal(state.ipProxyEnabled, true);
assert.equal(state.kiroRsUrl, 'https://kiro.example.com/admin');
@@ -435,7 +434,7 @@ function getRemovedKeys() {
`);
const persisted = await api.setPersistentSettings({
settingsSchemaVersion: 4,
settingsSchemaVersion: 5,
settingsState: {
activeFlowId: 'kiro',
services: {
@@ -445,8 +444,8 @@ function getRemovedKeys() {
},
flows: {
openai: {
integrationTargetId: 'cpa',
integrationTargets: {
selectedTargetId: 'cpa',
targets: {
cpa: {
vpsUrl: '',
vpsPassword: '',
@@ -481,7 +480,7 @@ function getRemovedKeys() {
},
},
kiro: {
targetId: 'kiro-rs',
selectedTargetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
@@ -499,21 +498,20 @@ function getRemovedKeys() {
const write = api.getPersistedWrites().at(-1);
assert.equal(persisted.activeFlowId, 'kiro');
assert.equal(persisted.kiroTargetId, 'kiro-rs');
assert.equal(persisted.targetId, 'kiro-rs');
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
assert.equal(persisted.kiroRsKey, 'nested-only-key');
assert.equal(persisted.plusAccountAccessStrategy, 'sub2api_codex_session');
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
assert.equal(persisted.settingsSchemaVersion, 4);
assert.equal(persisted.settingsSchemaVersion, 5);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'activeFlowId'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRsUrl'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRsKey'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
assert.equal(write.settingsSchemaVersion, 4);
assert.equal(write.settingsSchemaVersion, 5);
assert.equal(write.settingsState.activeFlowId, 'kiro');
assert.equal(write.settingsState.flows.openai.plus.plusAccountAccessStrategy, 'sub2api_codex_session');
assert.equal(write.settingsState.flows.kiro.targetId, 'kiro-rs');
assert.ok(api.getRemovedKeys().includes('panelMode'));
assert.equal(write.settingsState.flows.kiro.selectedTargetId, 'kiro-rs');
assert.ok(api.getRemovedKeys().includes('kiroRsUrl'));
});
@@ -526,7 +524,7 @@ const chrome = {
local: {
async get() {
return {
settingsSchemaVersion: 4,
settingsSchemaVersion: 5,
settingsState: {
activeFlowId: 'openai',
services: {
@@ -575,7 +573,7 @@ const chrome = {
local: {
async get() {
return {
settingsSchemaVersion: 4,
settingsSchemaVersion: 5,
settingsState: {
activeFlowId: 'openai',
services: {
@@ -585,8 +583,8 @@ const chrome = {
},
flows: {
openai: {
integrationTargetId: 'cpa',
integrationTargets: {
selectedTargetId: 'cpa',
targets: {
cpa: {
vpsUrl: 'https://old-cpa.example.com',
vpsPassword: 'old-vps-password',
@@ -621,7 +619,7 @@ const chrome = {
},
},
kiro: {
targetId: 'kiro-rs',
selectedTargetId: 'kiro-rs',
targets: {
'kiro-rs': {
baseUrl: 'https://kiro.example.com/admin',
@@ -654,7 +652,7 @@ function getRemovedKeys() {
`);
const persisted = await api.setPersistentSettings({
panelMode: 'sub2api',
targetId: 'sub2api',
mailProvider: 'cloudflare-temp-email',
ipProxyEnabled: true,
ipProxyMode: 'api',
@@ -664,7 +662,7 @@ function getRemovedKeys() {
});
const write = api.getPersistedWrites().at(-1);
assert.equal(persisted.panelMode, 'sub2api');
assert.equal(persisted.targetId, 'sub2api');
assert.equal(persisted.mailProvider, 'cloudflare-temp-email');
assert.equal(persisted.ipProxyEnabled, true);
assert.equal(persisted.ipProxyMode, 'api');
@@ -673,7 +671,7 @@ function getRemovedKeys() {
fromStep: 2,
toStep: 4,
});
assert.equal(write.settingsState.flows.openai.integrationTargetId, 'sub2api');
assert.equal(write.settingsState.flows.openai.selectedTargetId, 'sub2api');
assert.equal(write.settingsState.services.email.provider, 'cloudflare-temp-email');
assert.equal(write.settingsState.services.proxy.enabled, true);
assert.equal(write.settingsState.services.proxy.mode, 'api');
@@ -682,8 +680,8 @@ function getRemovedKeys() {
fromStep: 2,
toStep: 4,
});
assert.equal(write.settingsState.flows.openai.integrationTargets.sub2api.sub2apiUrl, 'https://sub2api.example.com');
assert.equal(write.settingsState.flows.openai.integrationTargets.sub2api.sub2apiEmail, 'owner@example.com');
assert.equal(write.settingsState.flows.openai.targets.sub2api.sub2apiUrl, 'https://sub2api.example.com');
assert.equal(write.settingsState.flows.openai.targets.sub2api.sub2apiEmail, 'owner@example.com');
assert.equal(write.settingsState.flows.kiro.targets['kiro-rs'].apiKey, 'kiro-key');
assert.equal(Object.prototype.hasOwnProperty.call(write, 'mailProvider'), false);
assert.equal(Object.prototype.hasOwnProperty.call(write, 'panelMode'), false);
+18 -18
View File
@@ -2,7 +2,7 @@
const assert = require('node:assert/strict');
const fs = require('node:fs');
const step2Source = fs.readFileSync('background/steps/submit-signup-email.js', 'utf8');
const step2Source = fs.readFileSync('flows/openai/background/steps/submit-signup-email.js', 'utf8');
const step2GlobalScope = {};
const step2Api = new Function('self', `${step2Source}; return self.MultiPageBackgroundStep2;`)(step2GlobalScope);
@@ -33,7 +33,7 @@ test('step 2 completes with password step skipped when landing on email verifica
isTabAlive: async () => true,
resolveSignupEmailForFlow: async () => 'user@example.com',
sendToContentScriptResilient: async () => ({ submitted: true }),
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({ email: 'user@example.com' });
@@ -72,7 +72,7 @@ test('step 2 keeps password flow when landing on password page', async () => {
isTabAlive: async () => true,
resolveSignupEmailForFlow: async () => 'user@example.com',
sendToContentScriptResilient: async () => ({ submitted: true }),
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({ email: 'user@example.com' });
@@ -150,7 +150,7 @@ test('step 2 uses phone activation when resolved signup method is phone', async
sentPayloads.push(message.payload);
return { submitted: true };
},
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({ signupMethod: 'phone' });
@@ -233,7 +233,7 @@ test('step 2 reuses existing signup phone activation without acquiring a new num
sentPayloads.push(message.payload);
return { submitted: true };
},
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({
@@ -290,7 +290,7 @@ test('step 2 submits manual signup phone without acquiring a number', async () =
sentPayloads.push(message.payload);
return { submitted: true };
},
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({
@@ -354,7 +354,7 @@ test('step 2 stops with an explicit error instead of silently skipping 3/4/5 on
isTabAlive: async () => true,
resolveSignupEmailForFlow: async () => 'user@example.com',
sendToContentScriptResilient: async () => ({ submitted: true }),
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await assert.rejects(
@@ -405,7 +405,7 @@ test('step 2 does not force auth-entry retry on logged-out chatgpt home when con
sentPayloads.push(message.payload);
return { submitted: true };
},
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
});
await executor.executeStep2({ email: 'user@example.com' });
@@ -472,7 +472,7 @@ test('step 2 waits for the existing signup tab to settle before probing the entr
}
return { submitted: true };
},
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabStableComplete: async (_tabId, options) => {
events.push({ type: 'wait-stable', options });
return { id: 17, url: 'https://chatgpt.com/', status: 'complete' };
@@ -543,7 +543,7 @@ test('signup flow helper recognizes email verification page as post-email landin
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => ({
id: 21,
url: 'https://auth.openai.com/email-verification',
@@ -600,7 +600,7 @@ test('signup flow helper waits for the signup entry tab to settle for step 2 bef
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabStableComplete: async (_tabId, options) => {
events.push({ type: 'wait-stable', options });
return { id: 23, url: 'https://chatgpt.com/', status: 'complete' };
@@ -680,7 +680,7 @@ test('signup flow helper accepts phone signup landing on login password page', a
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async (_tabId, predicate) => {
const url = 'https://auth.openai.com/log-in/password';
return predicate(url) ? { id: 22, url } : null;
@@ -724,7 +724,7 @@ test('signup flow helper reuses existing managed alias email when it is still co
setEmailCalls += 1;
},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => null,
});
@@ -767,7 +767,7 @@ test('signup flow helper can generate an email on demand when add-email starts f
setStateCalls.push(updates);
},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => null,
});
@@ -836,7 +836,7 @@ test('signup flow helper delegates preserved phone identity email sync to the sh
setStateCalls += 1;
},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: [],
OPENAI_AUTH_INJECT_FILES: [],
waitForTabUrlMatch: async () => null,
});
@@ -901,7 +901,7 @@ test('signup flow helper finalizes step 3 submit by reusing signup verification
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: ['content/utils.js', 'content/signup-page.js'],
OPENAI_AUTH_INJECT_FILES: ['content/utils.js', 'flows/openai/content/openai-auth.js'],
waitForTabUrlMatch: async () => null,
});
@@ -943,11 +943,11 @@ test('signup flow helper rewrites retryable step 3 finalize transport timeout in
isSignupPasswordPageUrl: () => true,
reuseOrCreateTab: async () => 31,
sendToContentScriptResilient: async () => {
throw new Error('Content script on signup-page did not respond in 45s. Try refreshing the tab and retry.');
throw new Error('Content script on openai-auth did not respond in 45s. Try refreshing the tab and retry.');
},
setEmailState: async () => {},
SIGNUP_ENTRY_URL: 'https://chatgpt.com/',
SIGNUP_PAGE_INJECT_FILES: ['content/utils.js', 'content/signup-page.js'],
OPENAI_AUTH_INJECT_FILES: ['content/utils.js', 'flows/openai/content/openai-auth.js'],
waitForTabUrlMatch: async () => null,
});
+15 -15
View File
@@ -6,22 +6,22 @@ test('background imports workflow step modules including rebuilt Kiro modules',
const source = fs.readFileSync('background.js', 'utf8');
[
'background/steps/open-chatgpt.js',
'background/steps/submit-signup-email.js',
'background/steps/fill-password.js',
'background/steps/fetch-signup-code.js',
'background/steps/fill-profile.js',
'background/steps/wait-registration-success.js',
'background/steps/oauth-login.js',
'background/steps/fetch-login-code.js',
'background/steps/confirm-oauth.js',
'background/steps/platform-verify.js',
'flows/openai/background/steps/open-chatgpt.js',
'flows/openai/background/steps/submit-signup-email.js',
'flows/openai/background/steps/fill-password.js',
'flows/openai/background/steps/fetch-signup-code.js',
'flows/openai/background/steps/fill-profile.js',
'flows/openai/background/steps/wait-registration-success.js',
'flows/openai/background/steps/oauth-login.js',
'flows/openai/background/steps/fetch-login-code.js',
'flows/openai/background/steps/confirm-oauth.js',
'flows/openai/background/steps/platform-verify.js',
'shared/kiro-timeouts.js',
'background/kiro/state.js',
'background/kiro/register-runner.js',
'background/kiro/desktop-client.js',
'background/kiro/desktop-authorize-runner.js',
'background/kiro/publisher-kiro-rs.js',
'flows/kiro/background/state.js',
'flows/kiro/background/register-runner.js',
'flows/kiro/background/desktop-client.js',
'flows/kiro/background/desktop-authorize-runner.js',
'flows/kiro/background/publisher-kiro-rs.js',
].forEach((path) => {
assert.match(source, new RegExp(path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
});
@@ -3,7 +3,7 @@ const assert = require('node:assert/strict');
const fs = require('node:fs');
test('background node registry preserves node metadata even before an executor is registered', () => {
const source = fs.readFileSync('background/steps/registry.js', 'utf8');
const source = fs.readFileSync('core/flow-kernel/step-registry.js', 'utf8');
const api = new Function('self', `${source}; return self.MultiPageBackgroundStepRegistry;`)({});
const registry = api.createNodeRegistry([
{
@@ -27,7 +27,7 @@ test('background node registry preserves node metadata even before an executor i
});
test('background node registry executes registered nodes in display order', async () => {
const source = fs.readFileSync('background/steps/registry.js', 'utf8');
const source = fs.readFileSync('core/flow-kernel/step-registry.js', 'utf8');
const api = new Function('self', `${source}; return self.MultiPageBackgroundStepRegistry;`)({});
const events = [];
const registry = api.createNodeRegistry([
+6 -6
View File
@@ -4,19 +4,19 @@ const fs = require('node:fs');
test('background imports node registry and wires the rebuilt Kiro executors', () => {
const source = fs.readFileSync('background.js', 'utf8');
assert.match(source, /background\/steps\/registry\.js/);
assert.match(source, /core\/flow-kernel\/step-registry\.js/);
assert.match(source, /data\/step-definitions\.js/);
assert.match(source, /background\/workflow-engine\.js/);
assert.match(source, /core\/flow-kernel\/workflow-engine\.js/);
assert.match(source, /MultiPageStepDefinitions\?\.getNodes/);
assert.match(source, /buildNodeRegistry\(definitions/);
assert.match(source, /const stepRegistryCache = new Map\(\);/);
assert.match(source, /const definitions = getNodeDefinitionsForState\(state\);/);
assert.match(source, /stepRegistryCache\.set\(cacheKey, buildStepRegistry\(definitions\)\)/);
assert.match(source, /background\/kiro\/register-runner\.js/);
assert.match(source, /background\/kiro\/desktop-client\.js/);
assert.match(source, /background\/kiro\/desktop-authorize-runner\.js/);
assert.match(source, /background\/kiro\/publisher-kiro-rs\.js/);
assert.match(source, /flows\/kiro\/background\/register-runner\.js/);
assert.match(source, /flows\/kiro\/background\/desktop-client\.js/);
assert.match(source, /flows\/kiro\/background\/desktop-authorize-runner\.js/);
assert.match(source, /flows\/kiro\/background\/publisher-kiro-rs\.js/);
assert.doesNotMatch(source, /background\/steps\/kiro-device-auth\.js/);
assert.match(source, /const kiroRegisterRunner = self\.MultiPageBackgroundKiroRegisterRunner\?\.createKiroRegisterRunner\(/);

Some files were not shown because too many files have changed in this diff Show More