Refactor auto-run flow state preservation
This commit is contained in:
+161
-7
@@ -892,7 +892,6 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|||||||
kiroSourceId: 'kiro-rs',
|
kiroSourceId: 'kiro-rs',
|
||||||
kiroRsUrl: self.MultiPageFlowRegistry?.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin',
|
kiroRsUrl: self.MultiPageFlowRegistry?.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin',
|
||||||
kiroRsKey: '',
|
kiroRsKey: '',
|
||||||
kiroRegion: self.MultiPageFlowRegistry?.DEFAULT_KIRO_REGION || 'us-east-1',
|
|
||||||
vpsUrl: '',
|
vpsUrl: '',
|
||||||
vpsPassword: '',
|
vpsPassword: '',
|
||||||
localCpaStep9Mode: DEFAULT_LOCAL_CPA_STEP9_MODE,
|
localCpaStep9Mode: DEFAULT_LOCAL_CPA_STEP9_MODE,
|
||||||
@@ -2815,12 +2814,6 @@ function normalizePersistentSettingValue(key, value) {
|
|||||||
).trim() || 'https://kiro.leftcode.xyz/admin';
|
).trim() || 'https://kiro.leftcode.xyz/admin';
|
||||||
case 'kiroRsKey':
|
case 'kiroRsKey':
|
||||||
return String(value || '');
|
return String(value || '');
|
||||||
case 'kiroRegion':
|
|
||||||
return String(
|
|
||||||
value
|
|
||||||
|| self.MultiPageFlowRegistry?.DEFAULT_KIRO_REGION
|
|
||||||
|| 'us-east-1'
|
|
||||||
).trim() || 'us-east-1';
|
|
||||||
case 'vpsUrl':
|
case 'vpsUrl':
|
||||||
return String(value || '').trim();
|
return String(value || '').trim();
|
||||||
case 'vpsPassword':
|
case 'vpsPassword':
|
||||||
@@ -3408,6 +3401,166 @@ async function getPersistedSettings() {
|
|||||||
return buildPersistentSettingsPayload(stored, { fillDefaults: true });
|
return buildPersistentSettingsPayload(stored, { fillDefaults: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cloneAutoRunKeepStateValue(value) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.map((entry) => cloneAutoRunKeepStateValue(entry));
|
||||||
|
}
|
||||||
|
if (isPlainObjectValue(value)) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(value).map(([key, entryValue]) => [key, cloneAutoRunKeepStateValue(entryValue)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeAutoRunKeepStateValue(baseValue, patchValue) {
|
||||||
|
if (Array.isArray(patchValue)) {
|
||||||
|
return patchValue.map((entry) => cloneAutoRunKeepStateValue(entry));
|
||||||
|
}
|
||||||
|
if (!isPlainObjectValue(patchValue)) {
|
||||||
|
return patchValue === undefined ? cloneAutoRunKeepStateValue(baseValue) : patchValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseObject = isPlainObjectValue(baseValue) ? baseValue : {};
|
||||||
|
const nextObject = {
|
||||||
|
...cloneAutoRunKeepStateValue(baseObject),
|
||||||
|
};
|
||||||
|
for (const [key, entryValue] of Object.entries(patchValue)) {
|
||||||
|
nextObject[key] = mergeAutoRunKeepStateValue(baseObject[key], entryValue);
|
||||||
|
}
|
||||||
|
return nextObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectAutoRunFreshResetRuntimeSettingKeys() {
|
||||||
|
const keySet = new Set();
|
||||||
|
const flowFieldGroups = isPlainObjectValue(runtimeStateHelpers?.FLOW_FIELD_GROUPS)
|
||||||
|
? runtimeStateHelpers.FLOW_FIELD_GROUPS
|
||||||
|
: {};
|
||||||
|
|
||||||
|
for (const groups of Object.values(flowFieldGroups)) {
|
||||||
|
if (!isPlainObjectValue(groups)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const fields of Object.values(groups)) {
|
||||||
|
if (!Array.isArray(fields)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const field of fields) {
|
||||||
|
const normalizedField = String(field || '').trim();
|
||||||
|
if (normalizedField) {
|
||||||
|
keySet.add(normalizedField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sharedRuntimeFieldGroups = [
|
||||||
|
runtimeStateHelpers?.RUNTIME_SHARED_FIELDS,
|
||||||
|
runtimeStateHelpers?.RUNTIME_PROXY_FIELDS,
|
||||||
|
];
|
||||||
|
for (const fields of sharedRuntimeFieldGroups) {
|
||||||
|
if (!Array.isArray(fields)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (const field of fields) {
|
||||||
|
const normalizedField = String(field || '').trim();
|
||||||
|
if (normalizedField) {
|
||||||
|
keySet.add(normalizedField);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return keySet;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFAULT_ACTIVE_FLOW_ID) {
|
||||||
|
const currentSettingsState = isPlainObjectValue(prevState?.settingsState)
|
||||||
|
? prevState.settingsState
|
||||||
|
: {};
|
||||||
|
const normalizedStepExecutionRangeByFlow = normalizeStepExecutionRangeByFlow(prevState?.stepExecutionRangeByFlow || {});
|
||||||
|
const nextSettingsStatePatch = {
|
||||||
|
activeFlowId,
|
||||||
|
services: {
|
||||||
|
email: {
|
||||||
|
provider: prevState?.mailProvider,
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
enabled: prevState?.ipProxyEnabled,
|
||||||
|
provider: prevState?.ipProxyService,
|
||||||
|
mode: prevState?.ipProxyMode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
flows: {
|
||||||
|
openai: {
|
||||||
|
source: {
|
||||||
|
selected: prevState?.panelMode,
|
||||||
|
},
|
||||||
|
autoRun: normalizedStepExecutionRangeByFlow.openai
|
||||||
|
? {
|
||||||
|
stepExecutionRange: normalizedStepExecutionRangeByFlow.openai,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
kiro: {
|
||||||
|
source: {
|
||||||
|
selected: prevState?.kiroSourceId,
|
||||||
|
},
|
||||||
|
autoRun: normalizedStepExecutionRangeByFlow.kiro
|
||||||
|
? {
|
||||||
|
stepExecutionRange: normalizedStepExecutionRangeByFlow.kiro,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return mergeAutoRunKeepStateValue(currentSettingsState, nextSettingsStatePatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFreshAutoRunKeepState(prevState = {}) {
|
||||||
|
const sourceState = isPlainObjectValue(prevState) ? prevState : {};
|
||||||
|
const activeFlowId = self.MultiPageFlowRegistry?.normalizeFlowId
|
||||||
|
? self.MultiPageFlowRegistry.normalizeFlowId(
|
||||||
|
sourceState.activeFlowId || sourceState.flowId,
|
||||||
|
DEFAULT_ACTIVE_FLOW_ID
|
||||||
|
)
|
||||||
|
: (String(sourceState.activeFlowId || sourceState.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase()
|
||||||
|
|| DEFAULT_ACTIVE_FLOW_ID);
|
||||||
|
const settingsState = buildAutoRunFreshResetSettingsState(sourceState, activeFlowId);
|
||||||
|
const persistedSnapshot = buildPersistentSettingsPayload({
|
||||||
|
...sourceState,
|
||||||
|
activeFlowId,
|
||||||
|
settingsState,
|
||||||
|
}, {
|
||||||
|
fillDefaults: false,
|
||||||
|
});
|
||||||
|
const runtimeOnlyKeys = collectAutoRunFreshResetRuntimeSettingKeys();
|
||||||
|
const keepState = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(persistedSnapshot)) {
|
||||||
|
if (runtimeOnlyKeys.has(key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
keepState[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
keepState.activeFlowId = activeFlowId;
|
||||||
|
keepState.flowId = activeFlowId;
|
||||||
|
if (Object.prototype.hasOwnProperty.call(sourceState, 'panelMode')) {
|
||||||
|
keepState.panelMode = normalizePanelMode(sourceState.panelMode);
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(sourceState, 'kiroSourceId')) {
|
||||||
|
keepState.kiroSourceId = self.MultiPageFlowRegistry?.normalizeSourceId
|
||||||
|
? self.MultiPageFlowRegistry.normalizeSourceId('kiro', sourceState.kiroSourceId, 'kiro-rs')
|
||||||
|
: String(sourceState.kiroSourceId || 'kiro-rs').trim().toLowerCase();
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(sourceState, 'settingsSchemaVersion')) {
|
||||||
|
keepState.settingsSchemaVersion = Number(sourceState.settingsSchemaVersion) || 0;
|
||||||
|
}
|
||||||
|
keepState.settingsState = settingsState;
|
||||||
|
return keepState;
|
||||||
|
}
|
||||||
|
|
||||||
async function getPersistedAliasState() {
|
async function getPersistedAliasState() {
|
||||||
try {
|
try {
|
||||||
const stored = await chrome.storage.local.get(PERSISTENT_ALIAS_STATE_KEYS);
|
const stored = await chrome.storage.local.get(PERSISTENT_ALIAS_STATE_KEYS);
|
||||||
@@ -11674,6 +11827,7 @@ const autoRunController = self.MultiPageBackgroundAutoRunController?.createAutoR
|
|||||||
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
|
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
|
||||||
broadcastAutoRunStatus,
|
broadcastAutoRunStatus,
|
||||||
broadcastStopToContentScripts,
|
broadcastStopToContentScripts,
|
||||||
|
buildFreshAutoRunKeepState,
|
||||||
cancelPendingCommands,
|
cancelPendingCommands,
|
||||||
clearStopRequest: () => clearStopRequest(),
|
clearStopRequest: () => clearStopRequest(),
|
||||||
createAutoRunSessionId: () => createAutoRunSessionId(),
|
createAutoRunSessionId: () => createAutoRunSessionId(),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
|
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS,
|
||||||
broadcastAutoRunStatus,
|
broadcastAutoRunStatus,
|
||||||
broadcastStopToContentScripts,
|
broadcastStopToContentScripts,
|
||||||
|
buildFreshAutoRunKeepState,
|
||||||
cancelPendingCommands,
|
cancelPendingCommands,
|
||||||
clearStopRequest,
|
clearStopRequest,
|
||||||
createAutoRunSessionId,
|
createAutoRunSessionId,
|
||||||
@@ -77,6 +78,67 @@
|
|||||||
throw new Error('自动运行节点执行器未接入。');
|
throw new Error('自动运行节点执行器未接入。');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildFreshStartStateSnapshot(state = {}) {
|
||||||
|
return {
|
||||||
|
...(state || {}),
|
||||||
|
currentNodeId: '',
|
||||||
|
nodeStatuses: {},
|
||||||
|
stepStatuses: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveFreshStartNodeId(state = {}) {
|
||||||
|
const freshState = buildFreshStartStateSnapshot(state);
|
||||||
|
return String(getFirstUnfinishedWorkflowNode(freshState) || '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildFreshAttemptKeepState(state = {}, context = {}) {
|
||||||
|
if (typeof buildFreshAutoRunKeepState === 'function') {
|
||||||
|
const helperPatch = buildFreshAutoRunKeepState(state, context);
|
||||||
|
if (helperPatch && typeof helperPatch === 'object' && !Array.isArray(helperPatch)) {
|
||||||
|
return {
|
||||||
|
...helperPatch,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
activeFlowId: state.activeFlowId,
|
||||||
|
flowId: state.flowId || state.activeFlowId,
|
||||||
|
panelMode: state.panelMode,
|
||||||
|
kiroSourceId: state.kiroSourceId,
|
||||||
|
vpsUrl: state.vpsUrl,
|
||||||
|
vpsPassword: state.vpsPassword,
|
||||||
|
customPassword: state.customPassword,
|
||||||
|
plusModeEnabled: state.plusModeEnabled,
|
||||||
|
plusPaymentMethod: state.plusPaymentMethod,
|
||||||
|
phoneVerificationEnabled: state.phoneVerificationEnabled,
|
||||||
|
phoneSignupReloginAfterBindEmailEnabled: state.phoneSignupReloginAfterBindEmailEnabled,
|
||||||
|
paypalEmail: state.paypalEmail,
|
||||||
|
paypalPassword: state.paypalPassword,
|
||||||
|
kiroRsUrl: state.kiroRsUrl,
|
||||||
|
kiroRsKey: state.kiroRsKey,
|
||||||
|
autoRunSkipFailures: state.autoRunSkipFailures,
|
||||||
|
autoRunFallbackThreadIntervalMinutes: state.autoRunFallbackThreadIntervalMinutes,
|
||||||
|
autoRunDelayEnabled: state.autoRunDelayEnabled,
|
||||||
|
autoRunDelayMinutes: state.autoRunDelayMinutes,
|
||||||
|
autoStepDelaySeconds: state.autoStepDelaySeconds,
|
||||||
|
stepExecutionRangeByFlow: state.stepExecutionRangeByFlow,
|
||||||
|
signupMethod: state.signupMethod,
|
||||||
|
mailProvider: state.mailProvider,
|
||||||
|
emailGenerator: state.emailGenerator,
|
||||||
|
gmailBaseEmail: state.gmailBaseEmail,
|
||||||
|
mail2925BaseEmail: state.mail2925BaseEmail,
|
||||||
|
currentMail2925AccountId: state.currentMail2925AccountId,
|
||||||
|
emailPrefix: state.emailPrefix,
|
||||||
|
inbucketHost: state.inbucketHost,
|
||||||
|
inbucketMailbox: state.inbucketMailbox,
|
||||||
|
cloudflareDomain: state.cloudflareDomain,
|
||||||
|
cloudflareDomains: state.cloudflareDomains,
|
||||||
|
reusablePhoneActivation: state.reusablePhoneActivation,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function createAutoRunRoundSummary(round) {
|
function createAutoRunRoundSummary(round) {
|
||||||
return {
|
return {
|
||||||
round,
|
round,
|
||||||
@@ -502,12 +564,13 @@
|
|||||||
autoRunAttemptRun: attemptRun,
|
autoRunAttemptRun: attemptRun,
|
||||||
});
|
});
|
||||||
roundSummary.attempts = attemptRun;
|
roundSummary.attempts = attemptRun;
|
||||||
const defaultStartNodeId = typeof runAutoSequenceFromNode === 'function' ? 'open-chatgpt' : 1;
|
const attemptState = await getState();
|
||||||
|
const defaultStartNodeId = resolveFreshStartNodeId(attemptState);
|
||||||
let startNodeId = defaultStartNodeId;
|
let startNodeId = defaultStartNodeId;
|
||||||
let useExistingProgress = false;
|
let useExistingProgress = false;
|
||||||
|
|
||||||
if (reuseExistingProgress) {
|
if (reuseExistingProgress) {
|
||||||
let currentState = await getState();
|
let currentState = attemptState;
|
||||||
if (getRunningWorkflowNodes(currentState).length) {
|
if (getRunningWorkflowNodes(currentState).length) {
|
||||||
currentState = await waitForRunningWorkflowNodesToFinish({
|
currentState = await waitForRunningWorkflowNodesToFinish({
|
||||||
currentRun: targetRun,
|
currentRun: targetRun,
|
||||||
@@ -525,32 +588,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!useExistingProgress) {
|
if (!useExistingProgress) {
|
||||||
const prevState = await getState();
|
const prevState = attemptState;
|
||||||
const keepSettings = {
|
const keepSettings = {
|
||||||
vpsUrl: prevState.vpsUrl,
|
...buildFreshAttemptKeepState(prevState, {
|
||||||
vpsPassword: prevState.vpsPassword,
|
targetRun,
|
||||||
customPassword: prevState.customPassword,
|
totalRuns,
|
||||||
plusModeEnabled: prevState.plusModeEnabled,
|
attemptRun,
|
||||||
paypalEmail: prevState.paypalEmail,
|
sessionId,
|
||||||
paypalPassword: prevState.paypalPassword,
|
}),
|
||||||
autoRunSkipFailures: prevState.autoRunSkipFailures,
|
|
||||||
autoRunFallbackThreadIntervalMinutes: prevState.autoRunFallbackThreadIntervalMinutes,
|
|
||||||
autoRunDelayEnabled: prevState.autoRunDelayEnabled,
|
|
||||||
autoRunDelayMinutes: prevState.autoRunDelayMinutes,
|
|
||||||
autoStepDelaySeconds: prevState.autoStepDelaySeconds,
|
|
||||||
stepExecutionRangeByFlow: prevState.stepExecutionRangeByFlow,
|
|
||||||
signupMethod: prevState.signupMethod,
|
|
||||||
mailProvider: prevState.mailProvider,
|
|
||||||
emailGenerator: prevState.emailGenerator,
|
|
||||||
gmailBaseEmail: prevState.gmailBaseEmail,
|
|
||||||
mail2925BaseEmail: prevState.mail2925BaseEmail,
|
|
||||||
currentMail2925AccountId: prevState.currentMail2925AccountId,
|
|
||||||
emailPrefix: prevState.emailPrefix,
|
|
||||||
inbucketHost: prevState.inbucketHost,
|
|
||||||
inbucketMailbox: prevState.inbucketMailbox,
|
|
||||||
cloudflareDomain: prevState.cloudflareDomain,
|
|
||||||
cloudflareDomains: prevState.cloudflareDomains,
|
|
||||||
reusablePhoneActivation: prevState.reusablePhoneActivation,
|
|
||||||
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
|
autoRunRoundSummaries: serializeAutoRunRoundSummaries(totalRuns, roundSummaries),
|
||||||
autoRunSessionId: sessionId,
|
autoRunSessionId: sessionId,
|
||||||
tabRegistry: {},
|
tabRegistry: {},
|
||||||
|
|||||||
@@ -191,6 +191,59 @@
|
|||||||
verifyHotmailAccount,
|
verifyHotmailAccount,
|
||||||
} = deps;
|
} = deps;
|
||||||
|
|
||||||
|
function normalizeMessageFlowId(value = '', fallback = 'openai') {
|
||||||
|
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||||
|
if (typeof rootScope.MultiPageFlowRegistry?.normalizeFlowId === 'function') {
|
||||||
|
return rootScope.MultiPageFlowRegistry.normalizeFlowId(value, fallback);
|
||||||
|
}
|
||||||
|
const fallbackFlowId = String(fallback || 'openai').trim().toLowerCase() || 'openai';
|
||||||
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
|
if (!normalized || normalized === 'codex') {
|
||||||
|
return fallbackFlowId;
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMessageSourceId(flowId, sourceId = '', fallback = '') {
|
||||||
|
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||||
|
if (typeof rootScope.MultiPageFlowRegistry?.normalizeSourceId === 'function') {
|
||||||
|
return rootScope.MultiPageFlowRegistry.normalizeSourceId(flowId, sourceId, fallback);
|
||||||
|
}
|
||||||
|
const fallbackSourceId = String(
|
||||||
|
fallback || (normalizeMessageFlowId(flowId) === 'kiro' ? 'kiro-rs' : 'cpa')
|
||||||
|
).trim().toLowerCase();
|
||||||
|
return String(sourceId || fallbackSourceId).trim().toLowerCase() || fallbackSourceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapAutoRunSourceIdToPanelMode(sourceId = '', fallback = 'cpa') {
|
||||||
|
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||||
|
if (typeof rootScope.MultiPageFlowRegistry?.mapSourceIdToPanelMode === 'function') {
|
||||||
|
return rootScope.MultiPageFlowRegistry.mapSourceIdToPanelMode('openai', sourceId, fallback);
|
||||||
|
}
|
||||||
|
return String(sourceId || fallback || 'cpa').trim().toLowerCase() || 'cpa';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildAutoRunFlowStateUpdates(payload = {}) {
|
||||||
|
const hasActiveFlowId = Object.prototype.hasOwnProperty.call(payload, 'activeFlowId');
|
||||||
|
const hasSourceId = Object.prototype.hasOwnProperty.call(payload, 'sourceId');
|
||||||
|
if (!hasActiveFlowId && !hasSourceId) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const activeFlowId = normalizeMessageFlowId(payload.activeFlowId, 'openai');
|
||||||
|
const updates = {
|
||||||
|
activeFlowId,
|
||||||
|
flowId: activeFlowId,
|
||||||
|
};
|
||||||
|
if (hasSourceId) {
|
||||||
|
if (activeFlowId === 'kiro') {
|
||||||
|
updates.kiroSourceId = normalizeMessageSourceId('kiro', payload.sourceId, 'kiro-rs');
|
||||||
|
} else {
|
||||||
|
updates.panelMode = mapAutoRunSourceIdToPanelMode(payload.sourceId, 'cpa');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updates;
|
||||||
|
}
|
||||||
|
|
||||||
function preserveKeyFromState(updates, currentState, key) {
|
function preserveKeyFromState(updates, currentState, key) {
|
||||||
if (!Object.prototype.hasOwnProperty.call(updates, key)) {
|
if (!Object.prototype.hasOwnProperty.call(updates, key)) {
|
||||||
return;
|
return;
|
||||||
@@ -1208,8 +1261,16 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const autoRunFlowStateUpdates = buildAutoRunFlowStateUpdates(message.payload || {});
|
||||||
|
if (Object.keys(autoRunFlowStateUpdates).length > 0 && typeof setState === 'function') {
|
||||||
|
await setState(autoRunFlowStateUpdates);
|
||||||
|
}
|
||||||
const state = await getState();
|
const state = await getState();
|
||||||
const autoRunStartValidation = validateAutoRunStart(state, { state });
|
const autoRunStartValidation = validateAutoRunStart(state, {
|
||||||
|
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
|
||||||
|
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
|
||||||
|
state,
|
||||||
|
});
|
||||||
if (autoRunStartValidation?.ok === false) {
|
if (autoRunStartValidation?.ok === false) {
|
||||||
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
|
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
|
||||||
}
|
}
|
||||||
@@ -1240,8 +1301,16 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const autoRunFlowStateUpdates = buildAutoRunFlowStateUpdates(message.payload || {});
|
||||||
|
if (Object.keys(autoRunFlowStateUpdates).length > 0 && typeof setState === 'function') {
|
||||||
|
await setState(autoRunFlowStateUpdates);
|
||||||
|
}
|
||||||
const state = await getState();
|
const state = await getState();
|
||||||
const autoRunStartValidation = validateAutoRunStart(state, { state });
|
const autoRunStartValidation = validateAutoRunStart(state, {
|
||||||
|
activeFlowId: autoRunFlowStateUpdates.activeFlowId ?? state?.activeFlowId,
|
||||||
|
panelMode: autoRunFlowStateUpdates.panelMode ?? state?.panelMode,
|
||||||
|
state,
|
||||||
|
});
|
||||||
if (autoRunStartValidation?.ok === false) {
|
if (autoRunStartValidation?.ok === false) {
|
||||||
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
|
throw new Error(autoRunStartValidation.errors?.[0]?.message || '当前设置不支持启动自动流程。');
|
||||||
}
|
}
|
||||||
@@ -1374,6 +1443,10 @@
|
|||||||
oauthFlowDeadlineSourceUrl: null,
|
oauthFlowDeadlineSourceUrl: null,
|
||||||
} : {}),
|
} : {}),
|
||||||
};
|
};
|
||||||
|
if (Object.prototype.hasOwnProperty.call(updates, 'activeFlowId')
|
||||||
|
&& !Object.prototype.hasOwnProperty.call(stateUpdates, 'flowId')) {
|
||||||
|
stateUpdates.flowId = updates.activeFlowId;
|
||||||
|
}
|
||||||
if (Object.prototype.hasOwnProperty.call(updates, 'icloudHostPreference')) {
|
if (Object.prototype.hasOwnProperty.call(updates, 'icloudHostPreference')) {
|
||||||
const nextHostPreference = String(updates.icloudHostPreference || '').trim().toLowerCase();
|
const nextHostPreference = String(updates.icloudHostPreference || '').trim().toLowerCase();
|
||||||
stateUpdates.preferredIcloudHost = nextHostPreference === 'icloud.com' || nextHostPreference === 'icloud.com.cn'
|
stateUpdates.preferredIcloudHost = nextHostPreference === 'icloud.com' || nextHostPreference === 'icloud.com.cn'
|
||||||
|
|||||||
@@ -321,13 +321,13 @@
|
|||||||
...cloneValue(normalizePlainObject(state.runtimeState)),
|
...cloneValue(normalizePlainObject(state.runtimeState)),
|
||||||
};
|
};
|
||||||
const activeFlowId = normalizeFlowId(
|
const activeFlowId = normalizeFlowId(
|
||||||
Object.prototype.hasOwnProperty.call(state, 'flowId')
|
Object.prototype.hasOwnProperty.call(state, 'activeFlowId')
|
||||||
? state.flowId
|
|
||||||
: Object.prototype.hasOwnProperty.call(state, 'activeFlowId')
|
|
||||||
? state.activeFlowId
|
? state.activeFlowId
|
||||||
: Object.prototype.hasOwnProperty.call(baseRuntimeState, 'flowId')
|
: Object.prototype.hasOwnProperty.call(state, 'flowId')
|
||||||
? baseRuntimeState.flowId
|
? state.flowId
|
||||||
: baseRuntimeState.activeFlowId
|
: Object.prototype.hasOwnProperty.call(baseRuntimeState, 'activeFlowId')
|
||||||
|
? baseRuntimeState.activeFlowId
|
||||||
|
: baseRuntimeState.flowId
|
||||||
);
|
);
|
||||||
const currentNodeId = String(
|
const currentNodeId = String(
|
||||||
Object.prototype.hasOwnProperty.call(state, 'currentNodeId')
|
Object.prototype.hasOwnProperty.call(state, 'currentNodeId')
|
||||||
|
|||||||
@@ -326,7 +326,7 @@
|
|||||||
try {
|
try {
|
||||||
const latestState = await getExecutionState(state);
|
const latestState = await getExecutionState(state);
|
||||||
const auth = await startBuilderIdDeviceLogin(
|
const auth = await startBuilderIdDeviceLogin(
|
||||||
latestState.kiroRegion || DEFAULT_REGION,
|
DEFAULT_REGION,
|
||||||
fetchImpl
|
fetchImpl
|
||||||
);
|
);
|
||||||
const loginUrl = cleanString(auth.verificationUriComplete || auth.verificationUri);
|
const loginUrl = cleanString(auth.verificationUriComplete || auth.verificationUri);
|
||||||
@@ -379,7 +379,7 @@
|
|||||||
const clientId = cleanString(latestState.kiroClientId);
|
const clientId = cleanString(latestState.kiroClientId);
|
||||||
const clientSecret = String(latestState.kiroClientSecret || '');
|
const clientSecret = String(latestState.kiroClientSecret || '');
|
||||||
const deviceCode = String(latestState.kiroDeviceAuthorizationCode || '');
|
const deviceCode = String(latestState.kiroDeviceAuthorizationCode || '');
|
||||||
const region = normalizeRegion(latestState.kiroAuthRegion || latestState.kiroRegion || DEFAULT_REGION);
|
const region = normalizeRegion(latestState.kiroAuthRegion, DEFAULT_REGION);
|
||||||
const expiresAt = Math.max(0, Number(latestState.kiroAuthExpiresAt) || 0);
|
const expiresAt = Math.max(0, Number(latestState.kiroAuthExpiresAt) || 0);
|
||||||
if (!clientId || !clientSecret || !deviceCode) {
|
if (!clientId || !clientSecret || !deviceCode) {
|
||||||
throw new Error('Kiro device login has not been started yet.');
|
throw new Error('Kiro device login has not been started yet.');
|
||||||
@@ -443,7 +443,7 @@
|
|||||||
const refreshToken = String(latestState.kiroRefreshToken || '');
|
const refreshToken = String(latestState.kiroRefreshToken || '');
|
||||||
const clientId = cleanString(latestState.kiroClientId);
|
const clientId = cleanString(latestState.kiroClientId);
|
||||||
const clientSecret = String(latestState.kiroClientSecret || '');
|
const clientSecret = String(latestState.kiroClientSecret || '');
|
||||||
const region = normalizeRegion(latestState.kiroAuthRegion || latestState.kiroRegion || DEFAULT_REGION);
|
const region = normalizeRegion(latestState.kiroAuthRegion, DEFAULT_REGION);
|
||||||
const kiroRsUrl = String(latestState.kiroRsUrl || '');
|
const kiroRsUrl = String(latestState.kiroRsUrl || '');
|
||||||
const kiroRsKey = String(latestState.kiroRsKey || '');
|
const kiroRsKey = String(latestState.kiroRsKey || '');
|
||||||
if (!refreshToken || !clientId || !clientSecret) {
|
if (!refreshToken || !clientId || !clientSecret) {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
const LEGACY_OPENAI_FLOW_ALIAS = 'codex';
|
const LEGACY_OPENAI_FLOW_ALIAS = 'codex';
|
||||||
const DEFAULT_OPENAI_SOURCE_ID = 'cpa';
|
const DEFAULT_OPENAI_SOURCE_ID = 'cpa';
|
||||||
const DEFAULT_KIRO_SOURCE_ID = 'kiro-rs';
|
const DEFAULT_KIRO_SOURCE_ID = 'kiro-rs';
|
||||||
const DEFAULT_KIRO_REGION = 'us-east-1';
|
|
||||||
const DEFAULT_KIRO_RS_URL = 'https://kiro.leftcode.xyz/admin';
|
const DEFAULT_KIRO_RS_URL = 'https://kiro.leftcode.xyz/admin';
|
||||||
const OPENAI_SOURCE_IDS = Object.freeze(['cpa', 'sub2api', 'codex2api']);
|
const OPENAI_SOURCE_IDS = Object.freeze(['cpa', 'sub2api', 'codex2api']);
|
||||||
const SHARED_SERVICE_IDS = Object.freeze(['email', 'proxy']);
|
const SHARED_SERVICE_IDS = Object.freeze(['email', 'proxy']);
|
||||||
@@ -310,7 +309,7 @@
|
|||||||
'kiro-source-kiro-rs': {
|
'kiro-source-kiro-rs': {
|
||||||
id: 'kiro-source-kiro-rs',
|
id: 'kiro-source-kiro-rs',
|
||||||
label: 'kiro.rs 配置',
|
label: 'kiro.rs 配置',
|
||||||
rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key', 'row-kiro-region'],
|
rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key'],
|
||||||
},
|
},
|
||||||
'kiro-runtime-status': {
|
'kiro-runtime-status': {
|
||||||
id: 'kiro-runtime-status',
|
id: 'kiro-runtime-status',
|
||||||
@@ -461,7 +460,6 @@
|
|||||||
return {
|
return {
|
||||||
DEFAULT_FLOW_CAPABILITIES,
|
DEFAULT_FLOW_CAPABILITIES,
|
||||||
DEFAULT_FLOW_ID,
|
DEFAULT_FLOW_ID,
|
||||||
DEFAULT_KIRO_REGION,
|
|
||||||
DEFAULT_KIRO_RS_URL,
|
DEFAULT_KIRO_RS_URL,
|
||||||
DEFAULT_KIRO_SOURCE_ID,
|
DEFAULT_KIRO_SOURCE_ID,
|
||||||
DEFAULT_OPENAI_SOURCE_ID,
|
DEFAULT_OPENAI_SOURCE_ID,
|
||||||
|
|||||||
@@ -35,7 +35,6 @@
|
|||||||
const defaultFlowId = String(deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai').trim().toLowerCase() || 'openai';
|
const defaultFlowId = String(deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai').trim().toLowerCase() || 'openai';
|
||||||
const defaultOpenAiSourceId = flowRegistry.DEFAULT_OPENAI_SOURCE_ID || 'cpa';
|
const defaultOpenAiSourceId = flowRegistry.DEFAULT_OPENAI_SOURCE_ID || 'cpa';
|
||||||
const defaultKiroSourceId = flowRegistry.DEFAULT_KIRO_SOURCE_ID || 'kiro-rs';
|
const defaultKiroSourceId = flowRegistry.DEFAULT_KIRO_SOURCE_ID || 'kiro-rs';
|
||||||
const defaultKiroRegion = flowRegistry.DEFAULT_KIRO_REGION || 'us-east-1';
|
|
||||||
const defaultKiroRsUrl = flowRegistry.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin';
|
const defaultKiroRsUrl = flowRegistry.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin';
|
||||||
const normalizeFlowId = typeof flowRegistry.normalizeFlowId === 'function'
|
const normalizeFlowId = typeof flowRegistry.normalizeFlowId === 'function'
|
||||||
? flowRegistry.normalizeFlowId
|
? flowRegistry.normalizeFlowId
|
||||||
@@ -123,7 +122,6 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
kiroRegion: defaultKiroRegion,
|
|
||||||
kiroRsPriority: 0,
|
kiroRsPriority: 0,
|
||||||
kiroRsEndpoint: '',
|
kiroRsEndpoint: '',
|
||||||
kiroRsAuthRegion: '',
|
kiroRsAuthRegion: '',
|
||||||
@@ -286,11 +284,6 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
kiroRegion: String(
|
|
||||||
input?.kiroRegion
|
|
||||||
?? nested?.flows?.kiro?.options?.kiroRegion
|
|
||||||
?? defaults.flows.kiro.options.kiroRegion
|
|
||||||
).trim() || defaults.flows.kiro.options.kiroRegion,
|
|
||||||
kiroRsPriority: Number(
|
kiroRsPriority: Number(
|
||||||
input?.kiroRsPriority
|
input?.kiroRsPriority
|
||||||
?? nested?.flows?.kiro?.options?.kiroRsPriority
|
?? nested?.flows?.kiro?.options?.kiroRsPriority
|
||||||
@@ -389,11 +382,11 @@
|
|||||||
next.ipProxyMode = normalizedState.services.proxy.mode;
|
next.ipProxyMode = normalizedState.services.proxy.mode;
|
||||||
next.kiroRsUrl = kiroState.source.entries['kiro-rs'].kiroRsUrl;
|
next.kiroRsUrl = kiroState.source.entries['kiro-rs'].kiroRsUrl;
|
||||||
next.kiroRsKey = kiroState.source.entries['kiro-rs'].kiroRsKey;
|
next.kiroRsKey = kiroState.source.entries['kiro-rs'].kiroRsKey;
|
||||||
next.kiroRegion = kiroState.options.kiroRegion;
|
|
||||||
next.kiroRsPriority = kiroState.options.kiroRsPriority;
|
next.kiroRsPriority = kiroState.options.kiroRsPriority;
|
||||||
next.kiroRsEndpoint = kiroState.options.kiroRsEndpoint;
|
next.kiroRsEndpoint = kiroState.options.kiroRsEndpoint;
|
||||||
next.kiroRsAuthRegion = kiroState.options.kiroRsAuthRegion;
|
next.kiroRsAuthRegion = kiroState.options.kiroRsAuthRegion;
|
||||||
next.kiroRsApiRegion = kiroState.options.kiroRsApiRegion;
|
next.kiroRsApiRegion = kiroState.options.kiroRsApiRegion;
|
||||||
|
delete next.kiroRegion;
|
||||||
next.stepExecutionRangeByFlow = buildStepExecutionRangeByFlow(normalizedState);
|
next.stepExecutionRangeByFlow = buildStepExecutionRangeByFlow(normalizedState);
|
||||||
next.settingsSchemaVersion = normalizedState.schemaVersion;
|
next.settingsSchemaVersion = normalizedState.schemaVersion;
|
||||||
next.settingsState = cloneValue(normalizedState);
|
next.settingsState = cloneValue(normalizedState);
|
||||||
@@ -409,7 +402,6 @@
|
|||||||
sourceId: getSelectedSourceId(normalizedState, 'kiro'),
|
sourceId: getSelectedSourceId(normalizedState, 'kiro'),
|
||||||
kiroRsUrl: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsUrl,
|
kiroRsUrl: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsUrl,
|
||||||
kiroRsKey: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsKey,
|
kiroRsKey: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsKey,
|
||||||
kiroRegion: normalizedState.flows.kiro.options.kiroRegion,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -257,10 +257,6 @@
|
|||||||
title="显示 kiro.rs API Key"></button>
|
title="显示 kiro.rs API Key"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="data-row" id="row-kiro-region" style="display:none;">
|
|
||||||
<span class="data-label">区域</span>
|
|
||||||
<input type="text" id="input-kiro-region" class="data-input" placeholder="us-east-1" />
|
|
||||||
</div>
|
|
||||||
<div class="data-row" id="row-kiro-device-code" style="display:none;">
|
<div class="data-row" id="row-kiro-device-code" style="display:none;">
|
||||||
<span class="data-label">设备码</span>
|
<span class="data-label">设备码</span>
|
||||||
<span id="display-kiro-device-code" class="data-value mono">未生成</span>
|
<span id="display-kiro-device-code" class="data-value mono">未生成</span>
|
||||||
|
|||||||
+34
-22
@@ -175,8 +175,6 @@ const rowKiroRsUrl = document.getElementById('row-kiro-rs-url');
|
|||||||
const inputKiroRsUrl = document.getElementById('input-kiro-rs-url');
|
const inputKiroRsUrl = document.getElementById('input-kiro-rs-url');
|
||||||
const rowKiroRsKey = document.getElementById('row-kiro-rs-key');
|
const rowKiroRsKey = document.getElementById('row-kiro-rs-key');
|
||||||
const inputKiroRsKey = document.getElementById('input-kiro-rs-key');
|
const inputKiroRsKey = document.getElementById('input-kiro-rs-key');
|
||||||
const rowKiroRegion = document.getElementById('row-kiro-region');
|
|
||||||
const inputKiroRegion = document.getElementById('input-kiro-region');
|
|
||||||
const rowKiroDeviceCode = document.getElementById('row-kiro-device-code');
|
const rowKiroDeviceCode = document.getElementById('row-kiro-device-code');
|
||||||
const displayKiroDeviceCode = document.getElementById('display-kiro-device-code');
|
const displayKiroDeviceCode = document.getElementById('display-kiro-device-code');
|
||||||
const rowKiroLoginUrl = document.getElementById('row-kiro-login-url');
|
const rowKiroLoginUrl = document.getElementById('row-kiro-login-url');
|
||||||
@@ -2589,15 +2587,32 @@ function shouldOfferAutoModeChoice(state = latestState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function syncLatestState(nextState) {
|
function syncLatestState(nextState) {
|
||||||
const mergedNodeStatuses = nextState?.nodeStatuses
|
const normalizedNextState = {
|
||||||
|
...(nextState || {}),
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
Object.prototype.hasOwnProperty.call(normalizedNextState, 'activeFlowId')
|
||||||
|
|| Object.prototype.hasOwnProperty.call(normalizedNextState, 'flowId')
|
||||||
|
) {
|
||||||
|
const fallbackFlowId = latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID;
|
||||||
|
const rawFlowId = Object.prototype.hasOwnProperty.call(normalizedNextState, 'activeFlowId')
|
||||||
|
? normalizedNextState.activeFlowId
|
||||||
|
: normalizedNextState.flowId;
|
||||||
|
const normalizedFlowId = typeof normalizeFlowId === 'function'
|
||||||
|
? normalizeFlowId(rawFlowId, fallbackFlowId)
|
||||||
|
: (String(rawFlowId || fallbackFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
|
||||||
|
normalizedNextState.activeFlowId = normalizedFlowId;
|
||||||
|
normalizedNextState.flowId = normalizedFlowId;
|
||||||
|
}
|
||||||
|
const mergedNodeStatuses = normalizedNextState?.nodeStatuses
|
||||||
? getStoredNodeStatuses({
|
? getStoredNodeStatuses({
|
||||||
nodeStatuses: { ...NODE_DEFAULT_STATUSES, ...(latestState?.nodeStatuses || {}), ...nextState.nodeStatuses },
|
nodeStatuses: { ...NODE_DEFAULT_STATUSES, ...(latestState?.nodeStatuses || {}), ...normalizedNextState.nodeStatuses },
|
||||||
})
|
})
|
||||||
: getStoredNodeStatuses(latestState);
|
: getStoredNodeStatuses(latestState);
|
||||||
|
|
||||||
latestState = {
|
latestState = {
|
||||||
...(latestState || {}),
|
...(latestState || {}),
|
||||||
...(nextState || {}),
|
...normalizedNextState,
|
||||||
nodeStatuses: mergedNodeStatuses,
|
nodeStatuses: mergedNodeStatuses,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -4153,9 +4168,6 @@ function collectSettingsPayload() {
|
|||||||
const defaultKiroRsUrl = String(
|
const defaultKiroRsUrl = String(
|
||||||
flowRegistryApi?.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin'
|
flowRegistryApi?.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin'
|
||||||
).trim() || 'https://kiro.leftcode.xyz/admin';
|
).trim() || 'https://kiro.leftcode.xyz/admin';
|
||||||
const defaultKiroRegion = String(
|
|
||||||
flowRegistryApi?.DEFAULT_KIRO_REGION || 'us-east-1'
|
|
||||||
).trim() || 'us-east-1';
|
|
||||||
const normalizeKiroSourceIdSafe = typeof normalizeSourceIdForFlow === 'function'
|
const normalizeKiroSourceIdSafe = typeof normalizeSourceIdForFlow === 'function'
|
||||||
? normalizeSourceIdForFlow
|
? normalizeSourceIdForFlow
|
||||||
: ((_flowId, sourceId = '', fallback = 'kiro-rs') => {
|
: ((_flowId, sourceId = '', fallback = 'kiro-rs') => {
|
||||||
@@ -4184,11 +4196,6 @@ function collectSettingsPayload() {
|
|||||||
|| latestState?.kiroRsKey
|
|| latestState?.kiroRsKey
|
||||||
|| ''
|
|| ''
|
||||||
),
|
),
|
||||||
kiroRegion: String(
|
|
||||||
(typeof inputKiroRegion !== 'undefined' && inputKiroRegion ? inputKiroRegion.value : '')
|
|
||||||
|| latestState?.kiroRegion
|
|
||||||
|| defaultKiroRegion
|
|
||||||
).trim() || defaultKiroRegion,
|
|
||||||
vpsUrl: inputVpsUrl.value.trim(),
|
vpsUrl: inputVpsUrl.value.trim(),
|
||||||
vpsPassword: inputVpsPassword.value,
|
vpsPassword: inputVpsPassword.value,
|
||||||
localCpaStep9Mode: getSelectedLocalCpaStep9Mode(),
|
localCpaStep9Mode: getSelectedLocalCpaStep9Mode(),
|
||||||
@@ -9753,7 +9760,7 @@ function applySettingsState(state) {
|
|||||||
signupMethod: normalizeSignupMethod(state?.signupMethod || DEFAULT_SIGNUP_METHOD),
|
signupMethod: normalizeSignupMethod(state?.signupMethod || DEFAULT_SIGNUP_METHOD),
|
||||||
};
|
};
|
||||||
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
|
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
|
||||||
activeFlowId: state?.flowId || state?.activeFlowId,
|
activeFlowId: state?.activeFlowId || state?.flowId,
|
||||||
plusPaymentMethod: state?.plusPaymentMethod,
|
plusPaymentMethod: state?.plusPaymentMethod,
|
||||||
signupMethod: stepDefinitionState.signupMethod,
|
signupMethod: stepDefinitionState.signupMethod,
|
||||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
|
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
|
||||||
@@ -9925,13 +9932,6 @@ function applySettingsState(state) {
|
|||||||
if (typeof inputKiroRsKey !== 'undefined' && inputKiroRsKey) {
|
if (typeof inputKiroRsKey !== 'undefined' && inputKiroRsKey) {
|
||||||
inputKiroRsKey.value = String(state?.kiroRsKey || '');
|
inputKiroRsKey.value = String(state?.kiroRsKey || '');
|
||||||
}
|
}
|
||||||
if (typeof inputKiroRegion !== 'undefined' && inputKiroRegion) {
|
|
||||||
inputKiroRegion.value = String(
|
|
||||||
state?.kiroRegion
|
|
||||||
|| getFlowRegistry()?.DEFAULT_KIRO_REGION
|
|
||||||
|| 'us-east-1'
|
|
||||||
).trim();
|
|
||||||
}
|
|
||||||
if (typeof displayKiroDeviceCode !== 'undefined' && displayKiroDeviceCode) {
|
if (typeof displayKiroDeviceCode !== 'undefined' && displayKiroDeviceCode) {
|
||||||
const kiroDeviceCode = String(
|
const kiroDeviceCode = String(
|
||||||
state?.flows?.kiro?.auth?.deviceCode
|
state?.flows?.kiro?.auth?.deviceCode
|
||||||
@@ -13456,6 +13456,16 @@ async function startAutoRunFromCurrentSettings() {
|
|||||||
inputRunCount.disabled = true;
|
inputRunCount.disabled = true;
|
||||||
const delayEnabled = inputAutoDelayEnabled.checked;
|
const delayEnabled = inputAutoDelayEnabled.checked;
|
||||||
const delayMinutes = normalizeAutoDelayMinutes(inputAutoDelayMinutes.value);
|
const delayMinutes = normalizeAutoDelayMinutes(inputAutoDelayMinutes.value);
|
||||||
|
const activeFlowId = typeof getSelectedFlowId === 'function'
|
||||||
|
? getSelectedFlowId(latestState)
|
||||||
|
: (String(latestState?.activeFlowId || latestState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID);
|
||||||
|
const sourceId = typeof getSelectedSourceId === 'function'
|
||||||
|
? getSelectedSourceId(activeFlowId)
|
||||||
|
: (
|
||||||
|
activeFlowId === DEFAULT_ACTIVE_FLOW_ID
|
||||||
|
? normalizePanelMode(latestState?.panelMode || 'cpa')
|
||||||
|
: (String(latestState?.kiroSourceId || 'kiro-rs').trim().toLowerCase() || 'kiro-rs')
|
||||||
|
);
|
||||||
inputAutoDelayMinutes.value = String(delayMinutes);
|
inputAutoDelayMinutes.value = String(delayMinutes);
|
||||||
btnAutoRun.innerHTML = delayEnabled
|
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> 计划中...'
|
? '<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> 计划中...'
|
||||||
@@ -13466,6 +13476,8 @@ async function startAutoRunFromCurrentSettings() {
|
|||||||
payload: {
|
payload: {
|
||||||
totalRuns,
|
totalRuns,
|
||||||
delayMinutes,
|
delayMinutes,
|
||||||
|
activeFlowId,
|
||||||
|
sourceId,
|
||||||
autoRunSkipFailures,
|
autoRunSkipFailures,
|
||||||
contributionMode: Boolean(latestState?.contributionMode),
|
contributionMode: Boolean(latestState?.contributionMode),
|
||||||
contributionNickname,
|
contributionNickname,
|
||||||
@@ -14053,7 +14065,7 @@ selectPanelMode.addEventListener('change', async () => {
|
|||||||
saveSettings({ silent: true }).catch(() => { });
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
});
|
});
|
||||||
|
|
||||||
[inputKiroRsUrl, inputKiroRsKey, inputKiroRegion].forEach((input) => {
|
[inputKiroRsUrl, inputKiroRsKey].forEach((input) => {
|
||||||
input?.addEventListener('input', () => {
|
input?.addEventListener('input', () => {
|
||||||
markSettingsDirty(true);
|
markSettingsDirty(true);
|
||||||
scheduleSettingsAutoSave();
|
scheduleSettingsAutoSave();
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
|
||||||
|
test('auto-run controller preserves kiro flow across fresh reset and starts from the kiro first node', async () => {
|
||||||
|
const source = fs.readFileSync('background/auto-run-controller.js', 'utf8');
|
||||||
|
const globalScope = {};
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundAutoRunController;`)(globalScope);
|
||||||
|
|
||||||
|
const executedNodeIds = [];
|
||||||
|
const kiroNodeIds = ['kiro-start-device-login', 'kiro-await-device-login', 'kiro-upload-credential'];
|
||||||
|
const openAiNodeIds = ['open-chatgpt', 'submit-signup-email', 'fill-password'];
|
||||||
|
let helperCalls = 0;
|
||||||
|
let sessionSeed = 700;
|
||||||
|
let currentState = {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
panelMode: 'cpa',
|
||||||
|
kiroSourceId: 'kiro-rs',
|
||||||
|
kiroRsUrl: 'https://kiro.example/admin',
|
||||||
|
kiroRsKey: 'demo-key',
|
||||||
|
kiroRsApiRegion: 'ap-east-1',
|
||||||
|
customFutureFlowField: 'future-ready',
|
||||||
|
plusModeEnabled: false,
|
||||||
|
plusPaymentMethod: 'paypal',
|
||||||
|
phoneVerificationEnabled: false,
|
||||||
|
phoneSignupReloginAfterBindEmailEnabled: false,
|
||||||
|
autoRunSkipFailures: false,
|
||||||
|
autoRunFallbackThreadIntervalMinutes: 0,
|
||||||
|
autoRunDelayEnabled: false,
|
||||||
|
autoRunDelayMinutes: 30,
|
||||||
|
autoStepDelaySeconds: null,
|
||||||
|
signupMethod: 'email',
|
||||||
|
stepExecutionRangeByFlow: {
|
||||||
|
openai: { enabled: false, fromStep: 1, toStep: 11 },
|
||||||
|
kiro: { enabled: false, fromStep: 1, toStep: 3 },
|
||||||
|
},
|
||||||
|
nodeStatuses: {
|
||||||
|
'open-chatgpt': 'stopped',
|
||||||
|
'kiro-start-device-login': 'pending',
|
||||||
|
'kiro-await-device-login': 'pending',
|
||||||
|
'kiro-upload-credential': 'pending',
|
||||||
|
},
|
||||||
|
tabRegistry: {
|
||||||
|
stale: { tabId: 99 },
|
||||||
|
},
|
||||||
|
sourceLastUrls: {
|
||||||
|
stale: 'https://chatgpt.com/',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const runtime = {
|
||||||
|
state: {
|
||||||
|
autoRunActive: false,
|
||||||
|
autoRunCurrentRun: 0,
|
||||||
|
autoRunTotalRuns: 1,
|
||||||
|
autoRunAttemptRun: 0,
|
||||||
|
autoRunSessionId: 0,
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return { ...this.state };
|
||||||
|
},
|
||||||
|
set(updates = {}) {
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
...updates,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const controller = api.createAutoRunController({
|
||||||
|
addLog: async () => {},
|
||||||
|
appendAccountRunRecord: async () => null,
|
||||||
|
AUTO_RUN_MAX_RETRIES_PER_ROUND: 3,
|
||||||
|
AUTO_RUN_RETRY_DELAY_MS: 3000,
|
||||||
|
AUTO_RUN_TIMER_KIND_BEFORE_RETRY: 'before_retry',
|
||||||
|
AUTO_RUN_TIMER_KIND_BETWEEN_ROUNDS: 'between_rounds',
|
||||||
|
broadcastAutoRunStatus: async (phase, payload = {}, extraState = {}) => {
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...extraState,
|
||||||
|
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
|
||||||
|
autoRunPhase: phase,
|
||||||
|
autoRunCurrentRun: payload.currentRun ?? currentState.autoRunCurrentRun ?? 0,
|
||||||
|
autoRunTotalRuns: payload.totalRuns ?? currentState.autoRunTotalRuns ?? 1,
|
||||||
|
autoRunAttemptRun: payload.attemptRun ?? currentState.autoRunAttemptRun ?? 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
broadcastStopToContentScripts: async () => {},
|
||||||
|
cancelPendingCommands: () => {},
|
||||||
|
clearStopRequest: () => {},
|
||||||
|
createAutoRunSessionId: () => {
|
||||||
|
sessionSeed += 1;
|
||||||
|
return sessionSeed;
|
||||||
|
},
|
||||||
|
buildFreshAutoRunKeepState: (prevState = {}, context = {}) => {
|
||||||
|
helperCalls += 1;
|
||||||
|
assert.equal(context.targetRun, 1);
|
||||||
|
assert.equal(context.attemptRun, 1);
|
||||||
|
return {
|
||||||
|
activeFlowId: prevState.activeFlowId,
|
||||||
|
flowId: prevState.activeFlowId,
|
||||||
|
panelMode: prevState.panelMode,
|
||||||
|
kiroSourceId: prevState.kiroSourceId,
|
||||||
|
kiroRsUrl: prevState.kiroRsUrl,
|
||||||
|
kiroRsKey: prevState.kiroRsKey,
|
||||||
|
kiroRsApiRegion: prevState.kiroRsApiRegion,
|
||||||
|
customFutureFlowField: prevState.customFutureFlowField,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
ensureHotmailMailboxReadyForAutoRunRound: async () => {},
|
||||||
|
getAutoRunStatusPayload: (phase, payload = {}) => ({
|
||||||
|
autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(phase),
|
||||||
|
autoRunPhase: phase,
|
||||||
|
autoRunCurrentRun: payload.currentRun ?? 0,
|
||||||
|
autoRunTotalRuns: payload.totalRuns ?? 1,
|
||||||
|
autoRunAttemptRun: payload.attemptRun ?? 0,
|
||||||
|
autoRunSessionId: payload.sessionId ?? 0,
|
||||||
|
}),
|
||||||
|
getErrorMessage: (error) => error?.message || String(error || ''),
|
||||||
|
getFirstUnfinishedNodeId: (statuses = {}, state = {}) => {
|
||||||
|
const flowId = String(state?.activeFlowId || state?.flowId || 'openai').trim().toLowerCase();
|
||||||
|
const candidateNodeIds = flowId === 'kiro' ? kiroNodeIds : openAiNodeIds;
|
||||||
|
for (const nodeId of candidateNodeIds) {
|
||||||
|
const status = String(statuses?.[nodeId] || 'pending').trim().toLowerCase();
|
||||||
|
if (!['completed', 'manual_completed', 'skipped'].includes(status)) {
|
||||||
|
return nodeId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
getPendingAutoRunTimerPlan: () => null,
|
||||||
|
getRunningNodeIds: () => [],
|
||||||
|
getState: async () => ({
|
||||||
|
...currentState,
|
||||||
|
nodeStatuses: { ...(currentState.nodeStatuses || {}) },
|
||||||
|
tabRegistry: { ...(currentState.tabRegistry || {}) },
|
||||||
|
sourceLastUrls: { ...(currentState.sourceLastUrls || {}) },
|
||||||
|
}),
|
||||||
|
getStopRequested: () => false,
|
||||||
|
hasSavedNodeProgress: () => false,
|
||||||
|
isAddPhoneAuthFailure: () => false,
|
||||||
|
isGpcTaskEndedFailure: () => false,
|
||||||
|
isPhoneSmsPlatformRateLimitFailure: () => false,
|
||||||
|
isPlusCheckoutNonFreeTrialFailure: () => false,
|
||||||
|
isRestartCurrentAttemptError: () => false,
|
||||||
|
isStep4Route405RecoveryLimitFailure: () => false,
|
||||||
|
isSignupUserAlreadyExistsFailure: () => false,
|
||||||
|
isStopError: () => false,
|
||||||
|
launchAutoRunTimerPlan: async () => false,
|
||||||
|
normalizeAutoRunFallbackThreadIntervalMinutes: (value) => Math.max(0, Number(value) || 0),
|
||||||
|
persistAutoRunTimerPlan: async () => {},
|
||||||
|
resetState: async () => {
|
||||||
|
currentState = {
|
||||||
|
activeFlowId: 'openai',
|
||||||
|
flowId: 'openai',
|
||||||
|
panelMode: 'cpa',
|
||||||
|
kiroSourceId: '',
|
||||||
|
kiroRsUrl: '',
|
||||||
|
kiroRsKey: '',
|
||||||
|
kiroRsApiRegion: '',
|
||||||
|
customFutureFlowField: '',
|
||||||
|
plusModeEnabled: false,
|
||||||
|
plusPaymentMethod: 'paypal',
|
||||||
|
phoneVerificationEnabled: false,
|
||||||
|
phoneSignupReloginAfterBindEmailEnabled: false,
|
||||||
|
autoRunSkipFailures: false,
|
||||||
|
autoRunFallbackThreadIntervalMinutes: 0,
|
||||||
|
autoRunDelayEnabled: false,
|
||||||
|
autoRunDelayMinutes: 30,
|
||||||
|
autoStepDelaySeconds: null,
|
||||||
|
signupMethod: 'email',
|
||||||
|
stepExecutionRangeByFlow: {
|
||||||
|
openai: { enabled: false, fromStep: 1, toStep: 11 },
|
||||||
|
kiro: { enabled: false, fromStep: 1, toStep: 3 },
|
||||||
|
},
|
||||||
|
nodeStatuses: {},
|
||||||
|
tabRegistry: {},
|
||||||
|
sourceLastUrls: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
runAutoSequenceFromNode: async (nodeId) => {
|
||||||
|
executedNodeIds.push(nodeId);
|
||||||
|
assert.equal(currentState.activeFlowId, 'kiro');
|
||||||
|
assert.equal(currentState.flowId, 'kiro');
|
||||||
|
assert.equal(currentState.kiroSourceId, 'kiro-rs');
|
||||||
|
assert.equal(currentState.kiroRsApiRegion, 'ap-east-1');
|
||||||
|
assert.equal(currentState.customFutureFlowField, 'future-ready');
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
nodeStatuses: {
|
||||||
|
'kiro-start-device-login': 'completed',
|
||||||
|
'kiro-await-device-login': 'completed',
|
||||||
|
'kiro-upload-credential': 'completed',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
runtime,
|
||||||
|
setState: async (updates = {}) => {
|
||||||
|
currentState = {
|
||||||
|
...currentState,
|
||||||
|
...updates,
|
||||||
|
nodeStatuses: updates.nodeStatuses
|
||||||
|
? { ...updates.nodeStatuses }
|
||||||
|
: currentState.nodeStatuses,
|
||||||
|
tabRegistry: updates.tabRegistry
|
||||||
|
? { ...updates.tabRegistry }
|
||||||
|
: currentState.tabRegistry,
|
||||||
|
sourceLastUrls: updates.sourceLastUrls
|
||||||
|
? { ...updates.sourceLastUrls }
|
||||||
|
: currentState.sourceLastUrls,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sleepWithStop: async () => {},
|
||||||
|
throwIfAutoRunSessionStopped: () => {},
|
||||||
|
waitForRunningNodesToFinish: async () => currentState,
|
||||||
|
chrome: {
|
||||||
|
runtime: {
|
||||||
|
sendMessage: () => Promise.resolve(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await controller.autoRunLoop(1, { autoRunSkipFailures: false, mode: 'restart' });
|
||||||
|
|
||||||
|
assert.deepStrictEqual(executedNodeIds, ['kiro-start-device-login']);
|
||||||
|
assert.equal(helperCalls, 1);
|
||||||
|
});
|
||||||
@@ -134,7 +134,6 @@ const FIVE_SIM_SUPPORTED_COUNTRY_ID_SET = new Set(['indonesia', 'thailand', 'vie
|
|||||||
const HERO_SMS_SUPPORTED_COUNTRY_ID_SET = new Set(['6', '52', '10']);
|
const HERO_SMS_SUPPORTED_COUNTRY_ID_SET = new Set(['6', '52', '10']);
|
||||||
const self = {
|
const self = {
|
||||||
MultiPageFlowRegistry: {
|
MultiPageFlowRegistry: {
|
||||||
DEFAULT_KIRO_REGION: 'us-east-1',
|
|
||||||
DEFAULT_KIRO_RS_URL: 'https://kiro.leftcode.xyz/admin',
|
DEFAULT_KIRO_RS_URL: 'https://kiro.leftcode.xyz/admin',
|
||||||
normalizeFlowId(value, fallback = 'openai') {
|
normalizeFlowId(value, fallback = 'openai') {
|
||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
@@ -279,7 +278,6 @@ return {
|
|||||||
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'kiro'), 'kiro');
|
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'kiro'), 'kiro');
|
||||||
assert.equal(api.normalizePersistentSettingValue('kiroSourceId', 'unknown'), 'kiro-rs');
|
assert.equal(api.normalizePersistentSettingValue('kiroSourceId', 'unknown'), 'kiro-rs');
|
||||||
assert.equal(api.normalizePersistentSettingValue('kiroRsUrl', ''), 'https://kiro.leftcode.xyz/admin');
|
assert.equal(api.normalizePersistentSettingValue('kiroRsUrl', ''), 'https://kiro.leftcode.xyz/admin');
|
||||||
assert.equal(api.normalizePersistentSettingValue('kiroRegion', ''), 'us-east-1');
|
|
||||||
assert.equal(api.normalizePersistentSettingValue('kiroRsKey', ' key-1 '), ' key-1 ');
|
assert.equal(api.normalizePersistentSettingValue('kiroRsKey', ' key-1 '), ' key-1 ');
|
||||||
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', '5SIM'), '5sim');
|
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', '5SIM'), '5sim');
|
||||||
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', 'NEXSMS'), 'nexsms');
|
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', 'NEXSMS'), 'nexsms');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const fs = require('node:fs');
|
|||||||
test('background imports auto-run controller module', () => {
|
test('background imports auto-run controller module', () => {
|
||||||
const source = fs.readFileSync('background.js', 'utf8');
|
const source = fs.readFileSync('background.js', 'utf8');
|
||||||
assert.match(source, /background\/auto-run-controller\.js/);
|
assert.match(source, /background\/auto-run-controller\.js/);
|
||||||
|
assert.match(source, /buildFreshAutoRunKeepState/);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('auto-run controller module exposes a factory', () => {
|
test('auto-run controller module exposes a factory', () => {
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ test('kiro start device login registers client, opens auth tab, and completes wi
|
|||||||
throw new Error(`Unexpected fetch URL: ${url}`);
|
throw new Error(`Unexpected fetch URL: ${url}`);
|
||||||
},
|
},
|
||||||
getState: async () => ({
|
getState: async () => ({
|
||||||
kiroRegion: 'eu-west-1',
|
|
||||||
}),
|
}),
|
||||||
registerTab: async (source, tabId) => {
|
registerTab: async (source, tabId) => {
|
||||||
registerCalls.push({ source, tabId });
|
registerCalls.push({ source, tabId });
|
||||||
@@ -94,12 +93,11 @@ test('kiro start device login registers client, opens auth tab, and completes wi
|
|||||||
|
|
||||||
await executor.executeKiroStartDeviceLogin({
|
await executor.executeKiroStartDeviceLogin({
|
||||||
nodeId: 'kiro-start-device-login',
|
nodeId: 'kiro-start-device-login',
|
||||||
kiroRegion: 'eu-west-1',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(fetchCalls.length, 2);
|
assert.equal(fetchCalls.length, 2);
|
||||||
assert.equal(fetchCalls[0].url, 'https://oidc.eu-west-1.amazonaws.com/client/register');
|
assert.equal(fetchCalls[0].url, 'https://oidc.us-east-1.amazonaws.com/client/register');
|
||||||
assert.equal(fetchCalls[1].url, 'https://oidc.eu-west-1.amazonaws.com/device_authorization');
|
assert.equal(fetchCalls[1].url, 'https://oidc.us-east-1.amazonaws.com/device_authorization');
|
||||||
assert.deepEqual(fetchCalls[1].body, {
|
assert.deepEqual(fetchCalls[1].body, {
|
||||||
clientId: 'client-001',
|
clientId: 'client-001',
|
||||||
clientSecret: 'secret-001',
|
clientSecret: 'secret-001',
|
||||||
@@ -120,7 +118,7 @@ test('kiro start device login registers client, opens auth tab, and completes wi
|
|||||||
assert.equal(finalState.kiroDeviceAuthorizationCode, 'device-code-001');
|
assert.equal(finalState.kiroDeviceAuthorizationCode, 'device-code-001');
|
||||||
assert.equal(finalState.kiroDeviceCode, 'ABCD-1234');
|
assert.equal(finalState.kiroDeviceCode, 'ABCD-1234');
|
||||||
assert.equal(finalState.kiroLoginUrl, 'https://device.example.com/complete');
|
assert.equal(finalState.kiroLoginUrl, 'https://device.example.com/complete');
|
||||||
assert.equal(finalState.kiroAuthRegion, 'eu-west-1');
|
assert.equal(finalState.kiroAuthRegion, 'us-east-1');
|
||||||
assert.equal(finalState.kiroAuthIntervalSeconds, 7);
|
assert.equal(finalState.kiroAuthIntervalSeconds, 7);
|
||||||
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
|
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
|
||||||
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
|
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
|
||||||
|
|||||||
@@ -296,6 +296,125 @@ test('SAVE_SETTING broadcasts operation delay setting without background success
|
|||||||
assert.equal(logs.length, 0);
|
assert.equal(logs.length, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('SAVE_SETTING mirrors activeFlowId into flowId when switching to kiro flow', async () => {
|
||||||
|
const source = fs.readFileSync('background/message-router.js', 'utf8');
|
||||||
|
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' };
|
||||||
|
|
||||||
|
const router = api.createMessageRouter({
|
||||||
|
addLog: async () => {},
|
||||||
|
buildLuckmailSessionSettingsPayload: () => ({}),
|
||||||
|
buildPersistentSettingsPayload: (input = {}) => Object.prototype.hasOwnProperty.call(input, 'activeFlowId')
|
||||||
|
? { activeFlowId: input.activeFlowId }
|
||||||
|
: {},
|
||||||
|
broadcastDataUpdate: (payload) => broadcasts.push(payload),
|
||||||
|
getState: async () => ({ ...state }),
|
||||||
|
setPersistentSettings: async () => {},
|
||||||
|
setState: async (updates) => { state = { ...state, ...updates }; },
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await router.handleMessage({
|
||||||
|
type: 'SAVE_SETTING',
|
||||||
|
source: 'sidepanel',
|
||||||
|
payload: { activeFlowId: 'kiro' },
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(response.ok, true);
|
||||||
|
assert.equal(state.activeFlowId, 'kiro');
|
||||||
|
assert.equal(state.flowId, 'kiro');
|
||||||
|
assert.deepStrictEqual(broadcasts.at(-1), {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
signupMethod: 'email',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('AUTO_RUN applies current flow selection from payload before starting loop', async () => {
|
||||||
|
const source = fs.readFileSync('background/message-router.js', 'utf8');
|
||||||
|
const globalScope = { console };
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundMessageRouter;`)(globalScope);
|
||||||
|
const calls = [];
|
||||||
|
const validations = [];
|
||||||
|
let state = {
|
||||||
|
activeFlowId: 'openai',
|
||||||
|
flowId: 'openai',
|
||||||
|
panelMode: 'cpa',
|
||||||
|
plusModeEnabled: false,
|
||||||
|
plusPaymentMethod: 'paypal',
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = api.createMessageRouter({
|
||||||
|
clearStopRequest: () => {},
|
||||||
|
getPendingAutoRunTimerPlan: () => null,
|
||||||
|
getState: async () => ({ ...state }),
|
||||||
|
normalizeRunCount: (value) => Number(value) || 1,
|
||||||
|
setState: async (updates) => {
|
||||||
|
calls.push({ type: 'setState', updates: { ...updates } });
|
||||||
|
state = { ...state, ...updates };
|
||||||
|
},
|
||||||
|
startAutoRunLoop: (totalRuns, options) => {
|
||||||
|
calls.push({ type: 'startAutoRunLoop', totalRuns, options });
|
||||||
|
},
|
||||||
|
validateAutoRunStart: (validationState, options = {}) => {
|
||||||
|
validations.push({
|
||||||
|
activeFlowId: validationState?.activeFlowId,
|
||||||
|
flowId: validationState?.flowId,
|
||||||
|
kiroSourceId: validationState?.kiroSourceId,
|
||||||
|
optionActiveFlowId: options?.activeFlowId,
|
||||||
|
});
|
||||||
|
return { ok: true, errors: [] };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await router.handleMessage({
|
||||||
|
type: 'AUTO_RUN',
|
||||||
|
payload: {
|
||||||
|
totalRuns: 1,
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
sourceId: 'kiro-rs',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(response.ok, true);
|
||||||
|
assert.equal(state.activeFlowId, 'kiro');
|
||||||
|
assert.equal(state.flowId, 'kiro');
|
||||||
|
assert.equal(state.kiroSourceId, 'kiro-rs');
|
||||||
|
assert.deepStrictEqual(calls, [
|
||||||
|
{
|
||||||
|
type: 'setState',
|
||||||
|
updates: {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
kiroSourceId: 'kiro-rs',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'setState',
|
||||||
|
updates: {
|
||||||
|
autoRunSkipFailures: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'startAutoRunLoop',
|
||||||
|
totalRuns: 1,
|
||||||
|
options: {
|
||||||
|
autoRunSkipFailures: false,
|
||||||
|
mode: 'restart',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
assert.deepStrictEqual(validations, [
|
||||||
|
{
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
kiroSourceId: 'kiro-rs',
|
||||||
|
optionActiveFlowId: 'kiro',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
test('SAVE_SETTING re-resolves signup method when panel mode changes', async () => {
|
test('SAVE_SETTING re-resolves signup method when panel mode changes', async () => {
|
||||||
const source = fs.readFileSync('background/message-router.js', 'utf8');
|
const source = fs.readFileSync('background/message-router.js', 'utf8');
|
||||||
const globalScope = { console };
|
const globalScope = { console };
|
||||||
|
|||||||
@@ -136,3 +136,34 @@ test('runtime-state patch accepts nested flow and node updates without legacy st
|
|||||||
assert.equal(patch.runtimeState.flowState.openai.auth.oauthUrl, 'https://new.example.com/start');
|
assert.equal(patch.runtimeState.flowState.openai.auth.oauthUrl, 'https://new.example.com/start');
|
||||||
assert.equal(patch.runtimeState.flowState.openai.plus.plusCheckoutTabId, 99);
|
assert.equal(patch.runtimeState.flowState.openai.plus.plusCheckoutTabId, 99);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId', () => {
|
||||||
|
const api = loadRuntimeStateApi();
|
||||||
|
const helpers = api.createRuntimeStateHelpers({
|
||||||
|
DEFAULT_ACTIVE_FLOW_ID: 'openai',
|
||||||
|
defaultNodeStatuses: {
|
||||||
|
'open-chatgpt': 'pending',
|
||||||
|
'kiro-start-device-login': 'pending',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const patch = helpers.buildSessionStatePatch({
|
||||||
|
flowId: 'openai',
|
||||||
|
activeFlowId: 'openai',
|
||||||
|
nodeStatuses: {
|
||||||
|
'open-chatgpt': 'completed',
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
nodeStatuses: {
|
||||||
|
'kiro-start-device-login': 'running',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(patch.activeFlowId, 'kiro');
|
||||||
|
assert.equal(patch.flowId, 'kiro');
|
||||||
|
assert.deepStrictEqual(patch.nodeStatuses, {
|
||||||
|
'open-chatgpt': 'pending',
|
||||||
|
'kiro-start-device-login': 'running',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|||||||
kiroSourceId: 'kiro-rs',
|
kiroSourceId: 'kiro-rs',
|
||||||
kiroRsUrl: 'https://kiro.leftcode.xyz/admin',
|
kiroRsUrl: 'https://kiro.leftcode.xyz/admin',
|
||||||
kiroRsKey: '',
|
kiroRsKey: '',
|
||||||
kiroRegion: 'us-east-1',
|
|
||||||
stepExecutionRangeByFlow: {},
|
stepExecutionRangeByFlow: {},
|
||||||
};
|
};
|
||||||
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
||||||
@@ -153,14 +152,13 @@ test('buildPersistentSettingsPayload writes canonical settings schema into persi
|
|||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsKey: 'secret-key',
|
kiroRsKey: 'secret-key',
|
||||||
kiroRegion: 'eu-west-1',
|
|
||||||
}, { fillDefaults: true });
|
}, { fillDefaults: true });
|
||||||
|
|
||||||
assert.equal(payload.activeFlowId, 'kiro');
|
assert.equal(payload.activeFlowId, 'kiro');
|
||||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.kiroRsKey, 'secret-key');
|
assert.equal(payload.kiroRsKey, 'secret-key');
|
||||||
assert.equal(payload.kiroRegion, 'eu-west-1');
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(payload.settingsSchemaVersion, 3);
|
||||||
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
||||||
assert.equal(payload.settingsState.flows.kiro.source.selected, 'kiro-rs');
|
assert.equal(payload.settingsState.flows.kiro.source.selected, 'kiro-rs');
|
||||||
@@ -205,7 +203,6 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
kiroRegion: 'eu-west-1',
|
|
||||||
},
|
},
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
||||||
@@ -219,7 +216,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
|||||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
||||||
assert.equal(payload.kiroRegion, 'eu-west-1');
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(payload.settingsSchemaVersion, 3);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -294,7 +291,6 @@ const chrome = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
kiroRegion: 'ap-southeast-1',
|
|
||||||
},
|
},
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
||||||
@@ -317,7 +313,7 @@ const chrome = {
|
|||||||
assert.equal(state.ipProxyEnabled, true);
|
assert.equal(state.ipProxyEnabled, true);
|
||||||
assert.equal(state.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(state.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(state.kiroRsKey, 'stored-key');
|
assert.equal(state.kiroRsKey, 'stored-key');
|
||||||
assert.equal(state.kiroRegion, 'ap-southeast-1');
|
assert.equal(Object.prototype.hasOwnProperty.call(state, 'kiroRegion'), false);
|
||||||
assert.deepEqual(state.stepExecutionRangeByFlow.kiro, {
|
assert.deepEqual(state.stepExecutionRangeByFlow.kiro, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
fromStep: 1,
|
fromStep: 1,
|
||||||
@@ -381,7 +377,6 @@ function getPersistedWrites() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
kiroRegion: 'us-west-2',
|
|
||||||
},
|
},
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
|
||||||
@@ -396,12 +391,12 @@ function getPersistedWrites() {
|
|||||||
assert.equal(persisted.activeFlowId, 'kiro');
|
assert.equal(persisted.activeFlowId, 'kiro');
|
||||||
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
||||||
assert.equal(persisted.kiroRegion, 'us-west-2');
|
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
|
||||||
assert.equal(persisted.settingsSchemaVersion, 3);
|
assert.equal(persisted.settingsSchemaVersion, 3);
|
||||||
assert.equal(write.activeFlowId, 'kiro');
|
assert.equal(write.activeFlowId, 'kiro');
|
||||||
assert.equal(write.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(write.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(write.kiroRsKey, 'nested-only-key');
|
assert.equal(write.kiroRsKey, 'nested-only-key');
|
||||||
assert.equal(write.kiroRegion, 'us-west-2');
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
|
||||||
assert.equal(write.settingsSchemaVersion, 3);
|
assert.equal(write.settingsSchemaVersion, 3);
|
||||||
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ test('settings schema normalizes flat input into canonical flow and service name
|
|||||||
ipProxyService: '711proxy',
|
ipProxyService: '711proxy',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsKey: 'secret-key',
|
kiroRsKey: 'secret-key',
|
||||||
kiroRegion: 'eu-west-1',
|
|
||||||
stepExecutionRangeByFlow: {
|
stepExecutionRangeByFlow: {
|
||||||
openai: { enabled: true, fromStep: 2, toStep: 9 },
|
openai: { enabled: true, fromStep: 2, toStep: 9 },
|
||||||
kiro: { enabled: true, fromStep: 1, toStep: 3 },
|
kiro: { enabled: true, fromStep: 1, toStep: 3 },
|
||||||
@@ -61,7 +60,6 @@ test('settings schema normalizes flat input into canonical flow and service name
|
|||||||
assert.equal(normalized.flows.kiro.source.selected, 'kiro-rs');
|
assert.equal(normalized.flows.kiro.source.selected, 'kiro-rs');
|
||||||
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsKey, 'secret-key');
|
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsKey, 'secret-key');
|
||||||
assert.equal(normalized.flows.kiro.options.kiroRegion, 'eu-west-1');
|
|
||||||
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
|
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
fromStep: 1,
|
fromStep: 1,
|
||||||
@@ -77,7 +75,6 @@ test('settings schema can project canonical state back to legacy payload without
|
|||||||
kiroSourceId: 'kiro-rs',
|
kiroSourceId: 'kiro-rs',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsKey: 'key-123',
|
kiroRsKey: 'key-123',
|
||||||
kiroRegion: 'ap-southeast-1',
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert.equal(payload.activeFlowId, 'kiro');
|
assert.equal(payload.activeFlowId, 'kiro');
|
||||||
@@ -85,7 +82,7 @@ test('settings schema can project canonical state back to legacy payload without
|
|||||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.kiroRsKey, 'key-123');
|
assert.equal(payload.kiroRsKey, 'key-123');
|
||||||
assert.equal(payload.kiroRegion, 'ap-southeast-1');
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(payload.settingsSchemaVersion, 3);
|
||||||
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -62,7 +62,14 @@ function createApi({
|
|||||||
|
|
||||||
return new Function(`
|
return new Function(`
|
||||||
const events = [];
|
const events = [];
|
||||||
const latestState = { contributionMode: false };
|
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
||||||
|
const latestState = {
|
||||||
|
contributionMode: false,
|
||||||
|
activeFlowId: 'openai',
|
||||||
|
flowId: 'openai',
|
||||||
|
panelMode: 'cpa',
|
||||||
|
kiroSourceId: 'kiro-rs',
|
||||||
|
};
|
||||||
const inputAutoSkipFailures = { checked: false };
|
const inputAutoSkipFailures = { checked: false };
|
||||||
const inputContributionNickname = { value: 'tester' };
|
const inputContributionNickname = { value: 'tester' };
|
||||||
const inputContributionQq = { value: '123456' };
|
const inputContributionQq = { value: '123456' };
|
||||||
@@ -71,6 +78,8 @@ const inputAutoDelayEnabled = { checked: false };
|
|||||||
const inputAutoDelayMinutes = { value: '30' };
|
const inputAutoDelayMinutes = { value: '30' };
|
||||||
const btnAutoRun = { disabled: false, innerHTML: '' };
|
const btnAutoRun = { disabled: false, innerHTML: '' };
|
||||||
const inputRunCount = { disabled: false };
|
const inputRunCount = { disabled: false };
|
||||||
|
const selectFlow = { value: latestState.activeFlowId };
|
||||||
|
const selectPanelMode = { value: latestState.panelMode };
|
||||||
let runCountValue = ${Math.max(1, Number(runCount) || 1)};
|
let runCountValue = ${Math.max(1, Number(runCount) || 1)};
|
||||||
let pendingAutoRunStartTotalRuns = 0;
|
let pendingAutoRunStartTotalRuns = 0;
|
||||||
let pendingAutoRunStartExpiresAt = 0;
|
let pendingAutoRunStartExpiresAt = 0;
|
||||||
@@ -103,6 +112,19 @@ async function persistCurrentSettingsForAction() {
|
|||||||
}
|
}
|
||||||
function getRunCountValue() { return Math.max(1, Number(runCountValue) || 1); }
|
function getRunCountValue() { return Math.max(1, Number(runCountValue) || 1); }
|
||||||
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
|
function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) || 0; }
|
||||||
|
function normalizePanelMode(value = '', fallback = 'cpa') {
|
||||||
|
return String(value || fallback || 'cpa').trim().toLowerCase() || 'cpa';
|
||||||
|
}
|
||||||
|
function getSelectedFlowId(state = latestState) {
|
||||||
|
return String(selectFlow.value || state.activeFlowId || state.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
|
||||||
|
}
|
||||||
|
function getSelectedSourceId(flowId = getSelectedFlowId()) {
|
||||||
|
return String(
|
||||||
|
flowId === 'kiro'
|
||||||
|
? (selectPanelMode.value || latestState.kiroSourceId || 'kiro-rs')
|
||||||
|
: normalizePanelMode(selectPanelMode.value || latestState.panelMode || 'cpa')
|
||||||
|
).trim().toLowerCase() || (flowId === 'kiro' ? 'kiro-rs' : 'cpa');
|
||||||
|
}
|
||||||
function shouldOfferAutoModeChoice() { return false; }
|
function shouldOfferAutoModeChoice() { return false; }
|
||||||
async function openAutoStartChoiceDialog() { throw new Error('should not be called'); }
|
async function openAutoStartChoiceDialog() { throw new Error('should not be called'); }
|
||||||
function getFirstUnfinishedStep() { return 1; }
|
function getFirstUnfinishedStep() { return 1; }
|
||||||
@@ -196,6 +218,26 @@ test('startAutoRunFromCurrentSettings freezes run count before async settings sy
|
|||||||
assert.equal(events[3].message.payload.totalRuns, 20);
|
assert.equal(events[3].message.payload.totalRuns, 20);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('startAutoRunFromCurrentSettings sends current flow selection with auto run payload', async () => {
|
||||||
|
const api = createApi({
|
||||||
|
persistImpl: `(events) => {
|
||||||
|
selectFlow.value = 'kiro';
|
||||||
|
selectPanelMode.value = 'kiro-rs';
|
||||||
|
latestState.activeFlowId = 'openai';
|
||||||
|
latestState.flowId = 'openai';
|
||||||
|
latestState.kiroSourceId = 'kiro-rs';
|
||||||
|
events.push({ type: 'flow-switch-race' });
|
||||||
|
}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await api.startAutoRunFromCurrentSettings();
|
||||||
|
const sendEvent = api.getEvents().find((entry) => entry.type === 'send');
|
||||||
|
|
||||||
|
assert.equal(result, true);
|
||||||
|
assert.equal(sendEvent.message.payload.activeFlowId, 'kiro');
|
||||||
|
assert.equal(sendEvent.message.payload.sourceId, 'kiro-rs');
|
||||||
|
});
|
||||||
|
|
||||||
test('startAutoRunFromCurrentSettings blocks when shared flow capability validation fails', async () => {
|
test('startAutoRunFromCurrentSettings blocks when shared flow capability validation fails', async () => {
|
||||||
const bundle = [
|
const bundle = [
|
||||||
extractFunction('normalizePendingAutoRunStartRunCount'),
|
extractFunction('normalizePendingAutoRunStartRunCount'),
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
|
|||||||
'id="label-source-selector"',
|
'id="label-source-selector"',
|
||||||
'id="row-kiro-rs-url"',
|
'id="row-kiro-rs-url"',
|
||||||
'id="row-kiro-rs-key"',
|
'id="row-kiro-rs-key"',
|
||||||
'id="row-kiro-region"',
|
|
||||||
'id="row-kiro-device-code"',
|
'id="row-kiro-device-code"',
|
||||||
'id="row-kiro-login-url"',
|
'id="row-kiro-login-url"',
|
||||||
'id="row-kiro-upload-status"',
|
'id="row-kiro-upload-status"',
|
||||||
@@ -114,6 +113,52 @@ return {
|
|||||||
assert.deepEqual(api.calls[1], { type: 'render', stepIds: [88] });
|
assert.deepEqual(api.calls[1], { type: 'render', stepIds: [88] });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('syncLatestState keeps activeFlowId and flowId in sync when only one side changes', () => {
|
||||||
|
const bundle = [
|
||||||
|
extractFunction(sidepanelSource, 'syncLatestState'),
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
const api = new Function(`
|
||||||
|
let latestState = {
|
||||||
|
activeFlowId: 'openai',
|
||||||
|
flowId: 'openai',
|
||||||
|
nodeStatuses: { 'open-chatgpt': 'completed' },
|
||||||
|
};
|
||||||
|
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
||||||
|
const NODE_DEFAULT_STATUSES = { 'open-chatgpt': 'pending' };
|
||||||
|
const calls = [];
|
||||||
|
function normalizeFlowId(value = '', fallback = DEFAULT_ACTIVE_FLOW_ID) {
|
||||||
|
return String(value || fallback || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
|
||||||
|
}
|
||||||
|
function getStoredNodeStatuses(state = {}) {
|
||||||
|
return { ...NODE_DEFAULT_STATUSES, ...(state?.nodeStatuses || {}) };
|
||||||
|
}
|
||||||
|
function renderAccountRecords(state) {
|
||||||
|
calls.push({ ...state });
|
||||||
|
}
|
||||||
|
${bundle}
|
||||||
|
return {
|
||||||
|
syncLatestState,
|
||||||
|
getLatestState() {
|
||||||
|
return latestState;
|
||||||
|
},
|
||||||
|
getCalls() {
|
||||||
|
return calls;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
`)();
|
||||||
|
|
||||||
|
api.syncLatestState({ flowId: 'kiro' });
|
||||||
|
|
||||||
|
assert.deepStrictEqual(api.getLatestState(), {
|
||||||
|
activeFlowId: 'kiro',
|
||||||
|
flowId: 'kiro',
|
||||||
|
nodeStatuses: { 'open-chatgpt': 'completed' },
|
||||||
|
});
|
||||||
|
assert.equal(api.getCalls()[0].activeFlowId, 'kiro');
|
||||||
|
assert.equal(api.getCalls()[0].flowId, 'kiro');
|
||||||
|
});
|
||||||
|
|
||||||
test('updatePanelModeUI reapplies dynamic Plus and phone visibility after flow group visibility', () => {
|
test('updatePanelModeUI reapplies dynamic Plus and phone visibility after flow group visibility', () => {
|
||||||
const bundle = [
|
const bundle = [
|
||||||
extractFunction(sidepanelSource, 'updatePanelModeUI'),
|
extractFunction(sidepanelSource, 'updatePanelModeUI'),
|
||||||
|
|||||||
+3
-3
@@ -44,7 +44,7 @@
|
|||||||
- 接收后台广播并更新 UI
|
- 接收后台广播并更新 UI
|
||||||
- 动态渲染步骤列表
|
- 动态渲染步骤列表
|
||||||
- 维护 `activeFlowId + sourceId` 的双层选择;`openai` flow 继续把 `panelMode` 作为 legacy 来源映射,`kiro` flow 则改用 `kiroSourceId`
|
- 维护 `activeFlowId + sourceId` 的双层选择;`openai` flow 继续把 `panelMode` 作为 legacy 来源映射,`kiro` flow 则改用 `kiroSourceId`
|
||||||
- 按当前 flow capability 决定显隐分组;Kiro flow 只显示来源下拉、`kiro.rs URL / API Key / region`、共享邮箱服务、共享 IP 代理与 Kiro 运行状态,不显示 OpenAI 接码 / Plus / 平台绑定配置
|
- 按当前 flow capability 决定显隐分组;Kiro flow 只显示来源下拉、`kiro.rs URL / API Key`、共享邮箱服务、共享 IP 代理与 Kiro 运行状态,不显示 OpenAI 接码 / Plus / 平台绑定配置
|
||||||
- 管理顶部“贡献”按钮与贡献模式主面板;贡献模式本身是 sidepanel 的运行态 UI 模式,不是新的 `panelMode` 来源
|
- 管理顶部“贡献”按钮与贡献模式主面板;贡献模式本身是 sidepanel 的运行态 UI 模式,不是新的 `panelMode` 来源
|
||||||
- 在贡献模式下复用同一套主自动流程启动,并在面板内展示贡献链路的 `OAUTH / 回调 / 总状态` 三块实时状态
|
- 在贡献模式下复用同一套主自动流程启动,并在面板内展示贡献链路的 `OAUTH / 回调 / 总状态` 三块实时状态
|
||||||
- 在顶部“贡献/使用”按钮下方展示一个非强制的内容更新轻提示;提示来源于 `flowpilot.qlhazycoder.top` 的公开公告 / 教程摘要,用户关闭后仅对当前 `promptVersion` 静默,下次内容版本变化后会重新出现
|
- 在顶部“贡献/使用”按钮下方展示一个非强制的内容更新轻提示;提示来源于 `flowpilot.qlhazycoder.top` 的公开公告 / 教程摘要,用户关闭后仅对当前 `promptVersion` 静默,下次内容版本变化后会重新出现
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
- 接码开关、接码平台 `phoneSmsProvider`,以及 HeroSMS / 5sim 各自的 API Key、国家/地区、价格上限和平台扩展设置
|
- 接码开关、接码平台 `phoneSmsProvider`,以及 HeroSMS / 5sim 各自的 API Key、国家/地区、价格上限和平台扩展设置
|
||||||
- iCloud 相关偏好
|
- iCloud 相关偏好
|
||||||
- LuckMail API 配置
|
- LuckMail API 配置
|
||||||
- Kiro 来源配置:`kiroRsUrl / kiroRsKey / kiroRegion / kiroRsPriority / kiroRsEndpoint / kiroRsAuthRegion / kiroRsApiRegion`
|
- Kiro 来源配置:`kiroRsUrl / kiroRsKey / kiroRsPriority / kiroRsEndpoint / kiroRsAuthRegion / kiroRsApiRegion`
|
||||||
- 自动运行默认配置
|
- 自动运行默认配置
|
||||||
- OAuth 授权后链总超时开关 `oauthFlowTimeoutEnabled`:默认开启;关闭后会立即清空已存在的 OAuth 总预算 deadline,仅保留各步骤本地等待超时
|
- OAuth 授权后链总超时开关 `oauthFlowTimeoutEnabled`:默认开启;关闭后会立即清空已存在的 OAuth 总预算 deadline,仅保留各步骤本地等待超时
|
||||||
- flow 级执行范围配置 `stepExecutionRangeByFlow`:按 flowId 保存,例如 `openai: { enabled: true, fromStep: 3, toStep: 6 }`;侧栏中的 `codex` 会归一到内部默认 flowId `openai`。这是通用配置结构,但当前只给 codex/openai flow 暴露 UI。
|
- flow 级执行范围配置 `stepExecutionRangeByFlow`:按 flowId 保存,例如 `openai: { enabled: true, fromStep: 3, toStep: 6 }`;侧栏中的 `codex` 会归一到内部默认 flowId `openai`。这是通用配置结构,但当前只给 codex/openai flow 暴露 UI。
|
||||||
@@ -631,7 +631,7 @@ Kiro flow 的目标不是复用 OpenAI 注册链,而是单独完成“拿 Buil
|
|||||||
链路如下:
|
链路如下:
|
||||||
|
|
||||||
1. sidepanel 把当前 flow 切到 `kiro`,来源固定为 `kiro-rs`
|
1. sidepanel 把当前 flow 切到 `kiro`,来源固定为 `kiro-rs`
|
||||||
2. 用户填写 `kiro.rs URL / API Key / region`
|
2. 用户填写 `kiro.rs URL / API Key`
|
||||||
3. 步骤 1 调用 AWS Builder ID OIDC:
|
3. 步骤 1 调用 AWS Builder ID OIDC:
|
||||||
- 注册 public client
|
- 注册 public client
|
||||||
- 请求 `device_authorization`
|
- 请求 `device_authorization`
|
||||||
|
|||||||
+1
-1
@@ -185,7 +185,7 @@
|
|||||||
Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;IP 代理配置保存、服务配置快照、开关应用和运行态回显由这里接入消息路由,具体解析/状态渲染下沉到 `sidepanel/ip-proxy-panel.js`;侧边栏初始化与点击“自动”前会刷新一次贡献站
|
Hotmail / 2925 的新增表单显隐统一接到 `sidepanel/account-pool-ui.js` 这一层共享 helper;IP 代理配置保存、服务配置快照、开关应用和运行态回显由这里接入消息路由,具体解析/状态渲染下沉到 `sidepanel/ip-proxy-panel.js`;侧边栏初始化与点击“自动”前会刷新一次贡献站
|
||||||
公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示;当前会保存、回显并热更新 `oauthFlowTimeoutEnabled` 设置;日志渲染只读取结构化 `entry.step` 生成步骤标签,不再正则解析日志正文;注册手机号输入框只同步运行态身份,不写入持久配置。
|
公开内容摘要,并按本地关闭版本决定是否展示轻提示,同时在首次初始化后按现有规则决定是否弹出新手引导提示;当前会保存、回显并热更新 `oauthFlowTimeoutEnabled` 设置;日志渲染只读取结构化 `entry.step` 生成步骤标签,不再正则解析日志正文;注册手机号输入框只同步运行态身份,不写入持久配置。
|
||||||
- `sidepanel/update-service.js`:侧边栏更新检查服务,负责 GitHub Releases 查询、`Ultra` / 历史 `Pro` / legacy `v` 版本族排序、缓存读取与版本展示。
|
- `sidepanel/update-service.js`:侧边栏更新检查服务,负责 GitHub Releases 查询、`Ultra` / 历史 `Pro` / legacy `v` 版本族排序、缓存读取与版本展示。
|
||||||
- 补充:`sidepanel/sidepanel.html` 与 `sidepanel/sidepanel.js` 当前已改为 flow-aware 侧边栏主界面;顶部提供 `flow/source` 双下拉,`openai` flow 继续把 `panelMode` 作为 legacy 来源映射,`kiro` flow 则改用 `kiroSourceId` 与 `kiro.rs URL / API Key / region` 专属字段,并只显示 Kiro 运行状态与共享邮箱/IP 代理配置。
|
- 补充:`sidepanel/sidepanel.html` 与 `sidepanel/sidepanel.js` 当前已改为 flow-aware 侧边栏主界面;顶部提供 `flow/source` 双下拉,`openai` flow 继续把 `panelMode` 作为 legacy 来源映射,`kiro` flow 则改用 `kiroSourceId` 与 `kiro.rs URL / API Key` 专属字段,并只显示 Kiro 运行状态与共享邮箱/IP 代理配置。
|
||||||
|
|
||||||
## `tests/`
|
## `tests/`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user