refactor kiro auth flow and share account password service

This commit is contained in:
QLHazyCoder
2026-05-18 12:49:49 +08:00
parent d081d47e1a
commit 12314e446a
21 changed files with 2155 additions and 108 deletions
+21 -5
View File
@@ -8,7 +8,15 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
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 kiroNodeIds = [
'kiro-start-device-login',
'kiro-submit-email',
'kiro-submit-name',
'kiro-submit-verification-code',
'kiro-fill-password',
'kiro-confirm-access',
'kiro-upload-credential',
];
const openAiNodeIds = ['open-chatgpt', 'submit-signup-email', 'fill-password'];
let helperCalls = 0;
let sessionSeed = 700;
@@ -33,12 +41,16 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
signupMethod: 'email',
stepExecutionRangeByFlow: {
openai: { enabled: false, fromStep: 1, toStep: 11 },
kiro: { enabled: false, fromStep: 1, toStep: 3 },
kiro: { enabled: false, fromStep: 1, toStep: 7 },
},
nodeStatuses: {
'open-chatgpt': 'stopped',
'kiro-start-device-login': 'pending',
'kiro-await-device-login': 'pending',
'kiro-submit-email': 'pending',
'kiro-submit-name': 'pending',
'kiro-submit-verification-code': 'pending',
'kiro-fill-password': 'pending',
'kiro-confirm-access': 'pending',
'kiro-upload-credential': 'pending',
},
tabRegistry: {
@@ -172,7 +184,7 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
signupMethod: 'email',
stepExecutionRangeByFlow: {
openai: { enabled: false, fromStep: 1, toStep: 11 },
kiro: { enabled: false, fromStep: 1, toStep: 3 },
kiro: { enabled: false, fromStep: 1, toStep: 7 },
},
nodeStatuses: {},
tabRegistry: {},
@@ -190,7 +202,11 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
...currentState,
nodeStatuses: {
'kiro-start-device-login': 'completed',
'kiro-await-device-login': 'completed',
'kiro-submit-email': 'completed',
'kiro-submit-name': 'completed',
'kiro-submit-verification-code': 'completed',
'kiro-fill-password': 'completed',
'kiro-confirm-access': 'completed',
'kiro-upload-credential': 'completed',
},
};
@@ -0,0 +1,23 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const source = fs.readFileSync('background.js', 'utf8');
const match = source.match(/function generatePassword\(\)\s*\{[\s\S]*?return passwordChars\.join\(''\);\r?\n\}/);
assert.ok(match, 'generatePassword definition should exist in background.js');
const generatePassword = new Function(`${match[0]}; return generatePassword;`)();
test('generatePassword produces shared account passwords within the required policy', () => {
for (let index = 0; index < 100; index += 1) {
const password = String(generatePassword() || '');
assert.ok(password.length >= 8, `password should be at least 8 characters: ${password}`);
assert.ok(password.length <= 64, `password should be at most 64 characters: ${password}`);
assert.match(password, /[A-Z]/, `password should include an uppercase letter: ${password}`);
assert.match(password, /[a-z]/, `password should include a lowercase letter: ${password}`);
assert.match(password, /[0-9]/, `password should include a digit: ${password}`);
assert.match(password, /[^A-Za-z0-9]/, `password should include a symbol: ${password}`);
}
});
@@ -23,6 +23,20 @@ function mergeUpdates(updatesList = []) {
return updatesList.reduce((acc, item) => Object.assign(acc, item), {});
}
function createChromeRecorder() {
const updates = [];
return {
updates,
chrome: {
tabs: {
update: async (tabId, update) => {
updates.push({ tabId, update });
},
},
},
};
}
test('kiro device auth module exposes a factory', () => {
const api = loadKiroDeviceAuthApi();
assert.equal(typeof api?.createKiroDeviceAuthExecutor, 'function');
@@ -30,19 +44,27 @@ test('kiro device auth module exposes a factory', () => {
assert.equal(typeof api?.uploadBuilderIdCredential, 'function');
});
test('kiro start device login registers client, opens auth tab, and completes with runtime payload', async () => {
test('kiro start device login opens the auth tab and waits for the email entry page', async () => {
const api = loadKiroDeviceAuthApi();
const fetchCalls = [];
const stateUpdates = [];
const registerCalls = [];
const reuseCalls = [];
const completeCalls = [];
const contentReadyCalls = [];
const contentMessages = [];
const stableWaitCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async (source, tabId, options = {}) => {
contentReadyCalls.push({ source, tabId, options });
},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({
url,
@@ -75,20 +97,35 @@ test('kiro start device login registers client, opens auth tab, and completes wi
}
throw new Error(`Unexpected fetch URL: ${url}`);
},
getState: async () => ({
}),
getState: async () => ({}),
registerTab: async (source, tabId) => {
registerCalls.push({ source, tabId });
},
KIRO_DEVICE_AUTH_INJECT_FILES: [
'shared/source-registry.js',
'content/utils.js',
'content/kiro-device-auth-page.js',
],
reuseOrCreateTab: async (source, url) => {
reuseCalls.push({ source, url });
return 88;
},
sendToContentScriptResilient: async (source, message, options = {}) => {
contentMessages.push({ source, message, options });
return {
ok: true,
state: 'email_entry',
url: 'https://device.example.com/complete',
};
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async (tabId, options = {}) => {
stableWaitCalls.push({ tabId, options });
},
});
await executor.executeKiroStartDeviceLogin({
@@ -111,6 +148,23 @@ test('kiro start device login registers client, opens auth tab, and completes wi
source: 'kiro-device-auth',
tabId: 88,
}]);
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.deepEqual(stableWaitCalls, [{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 2500,
initialDelayMs: 300,
},
}]);
assert.equal(contentReadyCalls.length, 1);
assert.equal(contentMessages.length, 1);
assert.equal(contentMessages[0].message.type, 'ENSURE_KIRO_PAGE_STATE');
assert.deepEqual(contentMessages[0].message.payload.targetStates, ['email_entry']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroClientId, 'client-001');
@@ -122,6 +176,8 @@ test('kiro start device login registers client, opens auth tab, and completes wi
assert.equal(finalState.kiroAuthIntervalSeconds, 7);
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
assert.equal(finalState.kiroFullName, '');
assert.equal(finalState.kiroVerificationRequestedAt, 0);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-start-device-login');
@@ -129,19 +185,428 @@ test('kiro start device login registers client, opens auth tab, and completes wi
assert.equal(completeCalls[0].payload.kiroLoginUrl, 'https://device.example.com/complete');
});
test('kiro await device login polls until refresh token is captured', async () => {
test('kiro submit email resolves the signup email, reactivates the auth tab, and waits for the name page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const resolvedEmails = [];
const contentMessages = [];
const stableWaitCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getState: async () => ({
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
email: '',
mailProvider: '163',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
KIRO_DEVICE_AUTH_INJECT_FILES: [
'shared/source-registry.js',
'content/utils.js',
'content/kiro-device-auth-page.js',
],
resolveSignupEmailForFlow: async (state, options = {}) => {
resolvedEmails.push({ state, options });
return 'user@example.com';
},
sendToContentScriptResilient: async (source, message, options = {}) => {
contentMessages.push({ source, message, options });
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'email_entry' : 'name_entry',
url: ensureCallIndex === 1
? 'https://device.example.com/complete'
: 'https://device.example.com/name',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'email_submitted',
url: 'https://device.example.com/complete',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async (tabId, options = {}) => {
stableWaitCalls.push({ tabId, options });
},
});
await executor.executeKiroSubmitEmail({
nodeId: 'kiro-submit-email',
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
email: '',
mailProvider: '163',
});
assert.equal(resolvedEmails.length, 1);
assert.equal(resolvedEmails[0].state.nodeId, 'kiro-submit-email');
assert.deepEqual(resolvedEmails[0].options, {
preserveAccountIdentity: true,
});
assert.deepEqual(tabUpdates, [
{ tabId: 88, update: { active: true } },
{ tabId: 88, update: { active: true } },
]);
assert.deepEqual(stableWaitCalls, [
{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 2500,
initialDelayMs: 300,
},
},
{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 1500,
initialDelayMs: 150,
},
},
]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].message.payload.targetStates, ['email_entry']);
assert.equal(contentMessages[1].message.nodeId, 'kiro-submit-email');
assert.deepEqual(contentMessages[1].message.payload, { email: 'user@example.com' });
assert.deepEqual(contentMessages[2].message.payload.targetStates, ['name_entry']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthorizedEmail, 'user@example.com');
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
assert.equal(finalState.kiroUploadError, '');
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
assert.equal(finalState.kiroFullName, '');
assert.equal(finalState.kiroVerificationRequestedAt, 0);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-email');
assert.equal(completeCalls[0].payload.email, 'user@example.com');
assert.equal(completeCalls[0].payload.accountIdentifierType, 'email');
assert.equal(completeCalls[0].payload.accountIdentifier, 'user@example.com');
assert.equal(completeCalls[0].payload.kiroNextState, 'name_entry');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/name');
});
test('kiro submit name generates a full name and waits for the otp page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const contentMessages = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
generateRandomName: () => ({ firstName: 'Ada', lastName: 'Lovelace' }),
getState: async () => ({
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'name_entry' : 'otp_page',
url: ensureCallIndex === 1
? 'https://device.example.com/name'
: 'https://device.example.com/verify',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'name_submitted',
url: 'https://device.example.com/name',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroSubmitName({
nodeId: 'kiro-submit-name',
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
});
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['name_entry']);
assert.equal(contentMessages[1].nodeId, 'kiro-submit-name');
assert.deepEqual(contentMessages[1].payload, { fullName: 'Ada Lovelace' });
assert.deepEqual(contentMessages[2].payload.targetStates, ['otp_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroFullName, 'Ada Lovelace');
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(typeof finalState.kiroVerificationRequestedAt, 'number');
assert.equal(finalState.kiroVerificationRequestedAt > 0, true);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-name');
assert.equal(completeCalls[0].payload.kiroFullName, 'Ada Lovelace');
assert.equal(completeCalls[0].payload.kiroNextState, 'otp_page');
});
test('kiro submit verification code polls mail, returns to the auth tab, and waits for the password page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const mailPollCalls = [];
const contentMessages = [];
const mailOpenCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getMailConfig: () => ({
source: 'mail-163',
url: 'https://mail.example.com/inbox',
label: '163 邮箱',
}),
getState: async () => ({
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
kiroVerificationRequestedAt: 1700000000000,
mailProvider: '163',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
reuseOrCreateTab: async (source, url) => {
mailOpenCalls.push({ source, url });
return 66;
},
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'otp_page' : 'password_page',
url: ensureCallIndex === 1
? 'https://device.example.com/verify'
: 'https://device.example.com/password',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'verification_submitted',
url: 'https://device.example.com/verify',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
sendToMailContentScriptResilient: async (mail, message, options = {}) => {
mailPollCalls.push({ mail, message, options });
return {
ok: true,
code: '654321',
emailTimestamp: 1700000005000,
mailId: 'mail-1',
};
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroSubmitVerificationCode({
nodeId: 'kiro-submit-verification-code',
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
kiroVerificationRequestedAt: 1700000000000,
mailProvider: '163',
});
assert.deepEqual(mailOpenCalls, [{
source: 'mail-163',
url: 'https://mail.example.com/inbox',
}]);
assert.equal(mailPollCalls.length, 1);
assert.equal(mailPollCalls[0].message.type, 'POLL_EMAIL');
assert.equal(mailPollCalls[0].message.payload.targetEmail, 'user@example.com');
assert.equal(mailPollCalls[0].message.payload.filterAfterTimestamp, 1700000000000);
assert.deepEqual(tabUpdates, [
{ tabId: 88, update: { active: true } },
{ tabId: 88, update: { active: true } },
]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['otp_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-submit-verification-code');
assert.deepEqual(contentMessages[1].payload, { code: '654321' });
assert.deepEqual(contentMessages[2].payload.targetStates, ['password_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-verification-code');
assert.equal(completeCalls[0].payload.code, '654321');
assert.equal(completeCalls[0].payload.mailId, 'mail-1');
assert.equal(completeCalls[0].payload.kiroNextState, 'password_page');
});
test('kiro fill password reuses the shared password state and waits for the page to leave password state', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const contentMessages = [];
const savedPasswords = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getState: async () => ({
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
customPassword: 'SharedPass123!',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
return {
ok: true,
state: 'password_page',
url: 'https://device.example.com/password',
};
}
if (message.type === 'ENSURE_KIRO_STATE_CHANGE') {
return {
ok: true,
state: 'authorization_page',
url: 'https://device.example.com/authorize',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'password_submitted',
url: 'https://device.example.com/password',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setPasswordState: async (password) => {
savedPasswords.push(password);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroFillPassword({
nodeId: 'kiro-fill-password',
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
customPassword: 'SharedPass123!',
});
assert.deepEqual(savedPasswords, ['SharedPass123!']);
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['password_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-fill-password');
assert.deepEqual(contentMessages[1].payload, { password: 'SharedPass123!' });
assert.deepEqual(contentMessages[2].payload.fromStates, ['password_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-fill-password');
assert.equal(completeCalls[0].payload.kiroNextState, 'authorization_page');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/authorize');
});
test('kiro confirm access completes the authorization page and then polls until refresh token is captured', async () => {
const api = loadKiroDeviceAuthApi();
const fetchCalls = [];
const stateUpdates = [];
const sleepCalls = [];
const completeCalls = [];
const contentMessages = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let pollCount = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({
url,
@@ -167,13 +632,35 @@ test('kiro await device login polls until refresh token is captured', async () =
});
},
getState: async () => ({
kiroAuthTabId: 88,
kiroClientId: 'client-001',
kiroClientSecret: 'secret-001',
kiroDeviceAuthorizationCode: 'device-code-001',
kiroAuthRegion: 'us-east-1',
kiroAuthExpiresAt: Date.now() + 60000,
kiroAuthIntervalSeconds: 5,
kiroLoginUrl: 'https://device.example.com/complete',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
return {
ok: true,
state: 'authorization_page',
url: 'https://device.example.com/authorize',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'success_page',
url: 'https://device.example.com/success',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
@@ -181,18 +668,28 @@ test('kiro await device login polls until refresh token is captured', async () =
sleepCalls.push(ms);
},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroAwaitDeviceLogin({
nodeId: 'kiro-await-device-login',
await executor.executeKiroConfirmAccess({
nodeId: 'kiro-confirm-access',
kiroAuthTabId: 88,
kiroClientId: 'client-001',
kiroClientSecret: 'secret-001',
kiroDeviceAuthorizationCode: 'device-code-001',
kiroAuthRegion: 'us-east-1',
kiroAuthExpiresAt: Date.now() + 60000,
kiroAuthIntervalSeconds: 5,
kiroLoginUrl: 'https://device.example.com/complete',
});
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 2);
assert.deepEqual(contentMessages[0].payload.targetStates, ['authorization_page', 'success_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-confirm-access');
assert.equal(fetchCalls.length, 2);
assert.equal(fetchCalls[0].url, 'https://oidc.us-east-1.amazonaws.com/token');
assert.deepEqual(fetchCalls[0].body, {
@@ -210,8 +707,10 @@ test('kiro await device login polls until refresh token is captured', async () =
assert.equal(finalState.kiroUploadStatus, 'ready_to_upload');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-await-device-login');
assert.equal(completeCalls[0].nodeId, 'kiro-confirm-access');
assert.equal(completeCalls[0].payload.kiroRefreshToken, 'refresh-001');
assert.equal(completeCalls[0].payload.kiroNextState, 'success_page');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/success');
});
test('kiro upload credential checks connection and uploads builder id credential to kiro.rs', async () => {
@@ -205,7 +205,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
options: {
},
autoRun: {
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
},
},
},
@@ -293,7 +293,7 @@ const chrome = {
options: {
},
autoRun: {
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
},
},
},
@@ -317,7 +317,7 @@ const chrome = {
assert.deepEqual(state.stepExecutionRangeByFlow.kiro, {
enabled: true,
fromStep: 1,
toStep: 3,
toStep: 7,
});
});
@@ -379,7 +379,7 @@ function getPersistedWrites() {
options: {
},
autoRun: {
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 3 },
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
},
},
},
+6 -2
View File
@@ -27,9 +27,13 @@ test('background imports node registry and shared workflow definitions', () => {
assert.match(source, /background\/steps\/kiro-device-auth\.js/);
assert.match(source, /const kiroDeviceAuthExecutor = self\.MultiPageBackgroundKiroDeviceAuth\?\.createKiroDeviceAuthExecutor\(/);
assert.match(source, /'kiro-start-device-login': \(state\) => kiroDeviceAuthExecutor\.executeKiroStartDeviceLogin\(state\)/);
assert.match(source, /'kiro-await-device-login': \(state\) => kiroDeviceAuthExecutor\.executeKiroAwaitDeviceLogin\(state\)/);
assert.match(source, /'kiro-submit-email': \(state\) => kiroDeviceAuthExecutor\.executeKiroSubmitEmail\(state\)/);
assert.match(source, /'kiro-submit-name': \(state\) => kiroDeviceAuthExecutor\.executeKiroSubmitName\(state\)/);
assert.match(source, /'kiro-submit-verification-code': \(state\) => kiroDeviceAuthExecutor\.executeKiroSubmitVerificationCode\(state\)/);
assert.match(source, /'kiro-fill-password': \(state\) => kiroDeviceAuthExecutor\.executeKiroFillPassword\(state\)/);
assert.match(source, /'kiro-confirm-access': \(state\) => kiroDeviceAuthExecutor\.executeKiroConfirmAccess\(state\)/);
assert.match(source, /'kiro-upload-credential': \(state\) => kiroDeviceAuthExecutor\.executeKiroUploadCredential\(state\)/);
assert.match(source, /'kiro-start-device-login',[\s\S]*'kiro-await-device-login',[\s\S]*'kiro-upload-credential'/);
assert.match(source, /'kiro-start-device-login',[\s\S]*'kiro-submit-email',[\s\S]*'kiro-submit-name',[\s\S]*'kiro-submit-verification-code',[\s\S]*'kiro-fill-password',[\s\S]*'kiro-confirm-access',[\s\S]*'kiro-upload-credential'/);
});
test('GoPay approve executor receives debugger click and manual OTP helpers', () => {
+1 -1
View File
@@ -98,7 +98,7 @@ test('flow capability registry exposes Kiro as an independent flow with its own
assert.equal(capabilityState.effectiveSourceId, 'kiro-rs');
assert.deepEqual(
capabilityState.visibleGroupIds,
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-email', 'service-proxy']
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-account', 'service-email', 'service-proxy']
);
});
+5 -3
View File
@@ -23,7 +23,7 @@ test('flow registry exposes openai and kiro with canonical source metadata', ()
assert.equal(flowRegistry.normalizeSourceId('kiro', 'anything-else'), 'kiro-rs');
assert.deepEqual(
flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'),
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-email', 'service-proxy']
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-account', 'service-email', 'service-proxy']
);
assert.deepEqual(
flowRegistry.getSettingsGroupDefinition('openai-plus')?.rowIds || [],
@@ -45,17 +45,19 @@ test('settings schema normalizes flat input into canonical flow and service name
mailProvider: 'hotmail',
ipProxyEnabled: true,
ipProxyService: '711proxy',
customPassword: 'SharedSecret123!',
kiroRsUrl: 'https://kiro.example.com/admin',
kiroRsKey: 'secret-key',
stepExecutionRangeByFlow: {
openai: { enabled: true, fromStep: 2, toStep: 9 },
kiro: { enabled: true, fromStep: 1, toStep: 3 },
kiro: { enabled: true, fromStep: 1, toStep: 7 },
},
});
assert.equal(normalized.activeFlowId, 'kiro');
assert.equal(normalized.services.email.provider, 'hotmail');
assert.equal(normalized.services.proxy.enabled, true);
assert.equal(normalized.services.account.customPassword, 'SharedSecret123!');
assert.equal(normalized.flows.openai.source.selected, 'sub2api');
assert.equal(normalized.flows.kiro.source.selected, 'kiro-rs');
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, 'https://kiro.example.com/admin');
@@ -63,7 +65,7 @@ test('settings schema normalizes flat input into canonical flow and service name
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
enabled: true,
fromStep: 1,
toStep: 3,
toStep: 7,
});
});
+1 -1
View File
@@ -203,7 +203,7 @@ function updatePhoneVerificationSettingsUI() {
}
function resolveCurrentSidepanelCapabilities() {
return {
visibleGroupIds: ['openai-account', 'openai-plus', 'openai-phone'],
visibleGroupIds: ['service-account', 'openai-plus', 'openai-phone'],
effectivePanelMode: 'cpa',
panelMode: 'cpa',
effectiveSourceId: 'cpa',
@@ -4,6 +4,7 @@ const fs = require('node:fs');
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
test('sidepanel exposes SUB2API account priority below group setting', () => {
assert.match(html, /id="row-sub2api-account-priority"/);
@@ -39,7 +40,8 @@ test('sidepanel persists and locks SUB2API account priority setting', () => {
source,
/inputSub2ApiAccountPriority\.value = String\(normalizeSub2ApiAccountPriorityValue\(state\?\.sub2apiAccountPriority\)\);/
);
assert.match(source, /rowSub2ApiAccountPriority\.style\.display = useSub2Api \? '' : 'none';/);
assert.match(source, /applyFlowSettingsGroupVisibility\(visibleGroupIds\);/);
assert.match(flowRegistrySource, /'openai-source-sub2api': \{[\s\S]*'row-sub2api-account-priority'/);
assert.match(source, /inputSub2ApiAccountPriority\.disabled = locked;/);
assert.match(
source,
+10
View File
@@ -66,5 +66,15 @@ test('shared source registry exposes canonical source, alias, detection, and rea
assert.equal(registry.driverAcceptsCommand('openai-auth', 'fetch-bind-email-code'), true);
assert.equal(registry.driverAcceptsCommand('content/platform-panel', 'platform-verify'), true);
assert.equal(registry.driverAcceptsCommand('openai-auth', 'platform-verify'), false);
assert.equal(registry.driverAcceptsCommand('content/kiro-device-auth-page', 'kiro-submit-email'), true);
assert.equal(registry.driverAcceptsCommand('content/kiro-device-auth-page', 'kiro-submit-name'), true);
assert.equal(registry.driverAcceptsCommand('content/kiro-device-auth-page', 'kiro-submit-verification-code'), true);
assert.equal(registry.driverAcceptsCommand('content/kiro-device-auth-page', 'kiro-fill-password'), true);
assert.equal(registry.driverAcceptsCommand('content/kiro-device-auth-page', 'kiro-confirm-access'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-start-device-login'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-submit-email'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-submit-name'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-submit-verification-code'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-fill-password'), true);
assert.equal(registry.driverAcceptsCommand('background/kiro-device-auth', 'kiro-confirm-access'), true);
});
+23 -7
View File
@@ -168,21 +168,37 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
kiroSteps.map((step) => step.key),
[
'kiro-start-device-login',
'kiro-await-device-login',
'kiro-submit-email',
'kiro-submit-name',
'kiro-submit-verification-code',
'kiro-fill-password',
'kiro-confirm-access',
'kiro-upload-credential',
]
);
assert.equal(kiroSteps.every((step) => step.flowId === 'kiro'), true);
assert.equal(kiroSteps[0].driverId, 'background/kiro-device-auth');
assert.equal(kiroSteps[2].sourceId, 'kiro-rs-admin');
assert.equal(kiroSteps[6].sourceId, 'kiro-rs-admin');
assert.equal(kiroSteps[0].title, '启动设备登录');
assert.equal(kiroSteps[1].title, '等待设备登录确认');
assert.equal(kiroSteps[2].title, '上传凭据到 kiro.rs');
assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'kiro' }), [1, 2, 3]);
assert.equal(api.getLastStepId({ activeFlowId: 'kiro' }), 3);
assert.equal(kiroSteps[1].title, '获取邮箱并继续');
assert.equal(kiroSteps[2].title, '填写姓名并继续');
assert.equal(kiroSteps[3].title, '获取验证码并继续');
assert.equal(kiroSteps[4].title, '设置密码并继续');
assert.equal(kiroSteps[5].title, '确认访问并授权');
assert.equal(kiroSteps[6].title, '上传凭据到 kiro.rs');
assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'kiro' }), [1, 2, 3, 4, 5, 6, 7]);
assert.equal(api.getLastStepId({ activeFlowId: 'kiro' }), 7);
assert.deepStrictEqual(
api.getNodes({ activeFlowId: 'kiro' }).map((node) => node.next),
[['kiro-await-device-login'], ['kiro-upload-credential'], []]
[
['kiro-submit-email'],
['kiro-submit-name'],
['kiro-submit-verification-code'],
['kiro-fill-password'],
['kiro-confirm-access'],
['kiro-upload-credential'],
[],
]
);
assert.equal(plusSteps[5].title, '创建 Plus Checkout');
assert.equal(plusSteps[7].title, 'PayPal 登录与授权');