feat: rebuild kiro flow as independent desktop auth pipeline
This commit is contained in:
@@ -9,12 +9,14 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
|
||||
const executedNodeIds = [];
|
||||
const kiroNodeIds = [
|
||||
'kiro-start-device-login',
|
||||
'kiro-open-register-page',
|
||||
'kiro-submit-email',
|
||||
'kiro-submit-name',
|
||||
'kiro-submit-verification-code',
|
||||
'kiro-fill-password',
|
||||
'kiro-confirm-access',
|
||||
'kiro-submit-password',
|
||||
'kiro-complete-register-consent',
|
||||
'kiro-start-desktop-authorize',
|
||||
'kiro-complete-desktop-authorize',
|
||||
'kiro-upload-credential',
|
||||
];
|
||||
const openAiNodeIds = ['open-chatgpt', 'submit-signup-email', 'fill-password'];
|
||||
@@ -24,10 +26,9 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
activeFlowId: 'kiro',
|
||||
flowId: 'kiro',
|
||||
panelMode: 'cpa',
|
||||
kiroSourceId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRsUrl: 'https://kiro.example/admin',
|
||||
kiroRsKey: 'demo-key',
|
||||
kiroRsApiRegion: 'ap-east-1',
|
||||
customFutureFlowField: 'future-ready',
|
||||
plusModeEnabled: false,
|
||||
plusPaymentMethod: 'paypal',
|
||||
@@ -41,16 +42,18 @@ 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: 7 },
|
||||
kiro: { enabled: false, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
nodeStatuses: {
|
||||
'open-chatgpt': 'stopped',
|
||||
'kiro-start-device-login': 'pending',
|
||||
'kiro-open-register-page': 'pending',
|
||||
'kiro-submit-email': 'pending',
|
||||
'kiro-submit-name': 'pending',
|
||||
'kiro-submit-verification-code': 'pending',
|
||||
'kiro-fill-password': 'pending',
|
||||
'kiro-confirm-access': 'pending',
|
||||
'kiro-submit-password': 'pending',
|
||||
'kiro-complete-register-consent': 'pending',
|
||||
'kiro-start-desktop-authorize': 'pending',
|
||||
'kiro-complete-desktop-authorize': 'pending',
|
||||
'kiro-upload-credential': 'pending',
|
||||
},
|
||||
tabRegistry: {
|
||||
@@ -113,10 +116,9 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
activeFlowId: prevState.activeFlowId,
|
||||
flowId: prevState.activeFlowId,
|
||||
panelMode: prevState.panelMode,
|
||||
kiroSourceId: prevState.kiroSourceId,
|
||||
kiroTargetId: prevState.kiroTargetId,
|
||||
kiroRsUrl: prevState.kiroRsUrl,
|
||||
kiroRsKey: prevState.kiroRsKey,
|
||||
kiroRsApiRegion: prevState.kiroRsApiRegion,
|
||||
customFutureFlowField: prevState.customFutureFlowField,
|
||||
};
|
||||
},
|
||||
@@ -167,10 +169,9 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
activeFlowId: 'openai',
|
||||
flowId: 'openai',
|
||||
panelMode: 'cpa',
|
||||
kiroSourceId: '',
|
||||
kiroTargetId: '',
|
||||
kiroRsUrl: '',
|
||||
kiroRsKey: '',
|
||||
kiroRsApiRegion: '',
|
||||
customFutureFlowField: '',
|
||||
plusModeEnabled: false,
|
||||
plusPaymentMethod: 'paypal',
|
||||
@@ -184,7 +185,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: 7 },
|
||||
kiro: { enabled: false, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
nodeStatuses: {},
|
||||
tabRegistry: {},
|
||||
@@ -195,18 +196,19 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
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.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(currentState.customFutureFlowField, 'future-ready');
|
||||
currentState = {
|
||||
...currentState,
|
||||
nodeStatuses: {
|
||||
'kiro-start-device-login': 'completed',
|
||||
'kiro-open-register-page': 'completed',
|
||||
'kiro-submit-email': 'completed',
|
||||
'kiro-submit-name': 'completed',
|
||||
'kiro-submit-verification-code': 'completed',
|
||||
'kiro-fill-password': 'completed',
|
||||
'kiro-confirm-access': 'completed',
|
||||
'kiro-submit-password': 'completed',
|
||||
'kiro-complete-register-consent': 'completed',
|
||||
'kiro-start-desktop-authorize': 'completed',
|
||||
'kiro-complete-desktop-authorize': 'completed',
|
||||
'kiro-upload-credential': 'completed',
|
||||
},
|
||||
};
|
||||
@@ -239,6 +241,6 @@ test('auto-run controller preserves kiro flow across fresh reset and starts from
|
||||
|
||||
await controller.autoRunLoop(1, { autoRunSkipFailures: false, mode: 'restart' });
|
||||
|
||||
assert.deepStrictEqual(executedNodeIds, ['kiro-start-device-login']);
|
||||
assert.deepStrictEqual(executedNodeIds, ['kiro-open-register-page']);
|
||||
assert.equal(helperCalls, 1);
|
||||
});
|
||||
|
||||
@@ -145,13 +145,13 @@ const self = {
|
||||
}
|
||||
return String(fallback || 'openai').trim().toLowerCase() === 'kiro' ? 'kiro' : 'openai';
|
||||
},
|
||||
normalizeSourceId(flowId, sourceId, fallback = 'kiro-rs') {
|
||||
normalizeTargetId(flowId, targetId, fallback = 'kiro-rs') {
|
||||
const normalizedFlowId = this.normalizeFlowId(flowId);
|
||||
if (normalizedFlowId !== 'kiro') {
|
||||
return 'cpa';
|
||||
}
|
||||
const normalizedSourceId = String(sourceId || '').trim().toLowerCase();
|
||||
return normalizedSourceId === 'kiro-rs' ? normalizedSourceId : fallback;
|
||||
const normalizedTargetId = String(targetId || '').trim().toLowerCase();
|
||||
return normalizedTargetId === 'kiro-rs' ? normalizedTargetId : fallback;
|
||||
},
|
||||
},
|
||||
GoPayUtils: {
|
||||
@@ -276,7 +276,7 @@ return {
|
||||
assert.equal(api.normalizePersistentSettingValue('signupMethod', 'unknown'), 'email');
|
||||
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'codex'), 'openai');
|
||||
assert.equal(api.normalizePersistentSettingValue('activeFlowId', 'kiro'), 'kiro');
|
||||
assert.equal(api.normalizePersistentSettingValue('kiroSourceId', 'unknown'), 'kiro-rs');
|
||||
assert.equal(api.normalizePersistentSettingValue('kiroTargetId', 'unknown'), 'kiro-rs');
|
||||
assert.equal(api.normalizePersistentSettingValue('kiroRsUrl', ''), 'https://kiro.leftcode.xyz/admin');
|
||||
assert.equal(api.normalizePersistentSettingValue('kiroRsKey', ' key-1 '), ' key-1 ');
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneSmsProvider', '5SIM'), '5sim');
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadDesktopAuthorizeRunnerApi() {
|
||||
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
|
||||
const clientSource = fs.readFileSync('background/kiro/desktop-client.js', 'utf8');
|
||||
const runnerSource = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8');
|
||||
const globalScope = {};
|
||||
new Function('self', `${stateSource}; ${clientSource}; ${runnerSource}; return self;`)(globalScope);
|
||||
return globalScope.MultiPageBackgroundKiroDesktopAuthorizeRunner;
|
||||
}
|
||||
|
||||
test('kiro desktop authorize runner exposes a factory and callback parser', () => {
|
||||
const api = loadDesktopAuthorizeRunnerApi();
|
||||
assert.equal(typeof api?.createKiroDesktopAuthorizeRunner, 'function');
|
||||
assert.equal(typeof api?.parseDesktopCallbackUrl, 'function');
|
||||
});
|
||||
|
||||
test('parseDesktopCallbackUrl validates state and redirect port', () => {
|
||||
const api = loadDesktopAuthorizeRunnerApi();
|
||||
|
||||
const success = api.parseDesktopCallbackUrl(
|
||||
'http://127.0.0.1:43121/oauth/callback?code=auth-code-001&state=state-001',
|
||||
'state-001',
|
||||
43121
|
||||
);
|
||||
assert.deepEqual(success, {
|
||||
url: 'http://127.0.0.1:43121/oauth/callback?code=auth-code-001&state=state-001',
|
||||
code: 'auth-code-001',
|
||||
state: 'state-001',
|
||||
});
|
||||
|
||||
const badState = api.parseDesktopCallbackUrl(
|
||||
'http://127.0.0.1:43121/oauth/callback?code=auth-code-001&state=wrong-state',
|
||||
'state-001',
|
||||
43121
|
||||
);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(badState, 'code'), false);
|
||||
assert.match(badState.error, /state/i);
|
||||
|
||||
const badPort = api.parseDesktopCallbackUrl(
|
||||
'http://127.0.0.1:43122/oauth/callback?code=auth-code-001&state=state-001',
|
||||
'state-001',
|
||||
43121
|
||||
);
|
||||
assert.equal(badPort, null);
|
||||
});
|
||||
@@ -1,880 +0,0 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadKiroDeviceAuthApi() {
|
||||
const source = fs.readFileSync('background/steps/kiro-device-auth.js', 'utf8');
|
||||
return new Function('self', `${source}; return self.MultiPageBackgroundKiroDeviceAuth;`)({});
|
||||
}
|
||||
|
||||
function createResponse({ ok = true, status = 200, json = null, text = '' } = {}) {
|
||||
const bodyText = text || (json ? JSON.stringify(json) : '');
|
||||
return {
|
||||
ok,
|
||||
status,
|
||||
statusText: bodyText || `HTTP ${status}`,
|
||||
async text() {
|
||||
return bodyText;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
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');
|
||||
assert.equal(typeof api?.startBuilderIdDeviceLogin, 'function');
|
||||
assert.equal(typeof api?.uploadBuilderIdCredential, 'function');
|
||||
});
|
||||
|
||||
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 removedCookies = [];
|
||||
const browsingDataCalls = [];
|
||||
const { chrome, updates: tabUpdates } = createChromeRecorder();
|
||||
chrome.cookies = {
|
||||
getAllCookieStores: async () => [{ id: 'store-a' }],
|
||||
getAll: async () => [
|
||||
{ domain: '.view.awsapps.com', path: '/start', name: 'awsapps', storeId: 'store-a' },
|
||||
{ domain: '.oidc.us-east-1.amazonaws.com', path: '/', name: 'oidc', storeId: 'store-a' },
|
||||
{ domain: '.signin.aws', path: '/', name: 'signin', storeId: 'store-a' },
|
||||
{ domain: '.profile.aws.amazon.com', path: '/', name: 'profile-amazon', storeId: 'store-a' },
|
||||
{ domain: '.example.com', path: '/', name: 'keep', storeId: 'store-a' },
|
||||
],
|
||||
remove: async (details) => {
|
||||
removedCookies.push(details);
|
||||
return details;
|
||||
},
|
||||
};
|
||||
chrome.browsingData = {
|
||||
removeCookies: async (details) => {
|
||||
browsingDataCalls.push(details);
|
||||
},
|
||||
};
|
||||
|
||||
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,
|
||||
method: options.method || 'GET',
|
||||
body: options.body ? JSON.parse(options.body) : null,
|
||||
});
|
||||
if (url.endsWith('/client/register')) {
|
||||
return createResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: {
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
},
|
||||
});
|
||||
}
|
||||
if (url.endsWith('/device_authorization')) {
|
||||
return createResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: {
|
||||
deviceCode: 'device-code-001',
|
||||
userCode: 'ABCD-1234',
|
||||
verificationUri: 'https://device.example.com/start',
|
||||
verificationUriComplete: 'https://device.example.com/complete',
|
||||
interval: 7,
|
||||
expiresIn: 900,
|
||||
},
|
||||
});
|
||||
}
|
||||
throw new Error(`Unexpected fetch URL: ${url}`);
|
||||
},
|
||||
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({
|
||||
nodeId: 'kiro-start-device-login',
|
||||
});
|
||||
|
||||
assert.deepEqual(removedCookies, [
|
||||
{
|
||||
url: 'https://view.awsapps.com/start',
|
||||
name: 'awsapps',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://oidc.us-east-1.amazonaws.com/',
|
||||
name: 'oidc',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://signin.aws/',
|
||||
name: 'signin',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
{
|
||||
url: 'https://profile.aws.amazon.com/',
|
||||
name: 'profile-amazon',
|
||||
storeId: 'store-a',
|
||||
},
|
||||
]);
|
||||
assert.deepEqual(browsingDataCalls, [{
|
||||
since: 0,
|
||||
origins: [
|
||||
'https://view.awsapps.com',
|
||||
'https://login.awsapps.com',
|
||||
'https://oidc.us-east-1.amazonaws.com',
|
||||
'https://signin.aws',
|
||||
'https://signin.aws.amazon.com',
|
||||
'https://profile.aws',
|
||||
'https://profile.aws.amazon.com',
|
||||
],
|
||||
}]);
|
||||
assert.equal(fetchCalls.length, 2);
|
||||
assert.equal(fetchCalls[0].url, 'https://oidc.us-east-1.amazonaws.com/client/register');
|
||||
assert.equal(fetchCalls[1].url, 'https://oidc.us-east-1.amazonaws.com/device_authorization');
|
||||
assert.deepEqual(fetchCalls[1].body, {
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
startUrl: 'https://view.awsapps.com/start',
|
||||
});
|
||||
assert.deepEqual(reuseCalls, [{
|
||||
source: 'kiro-device-auth',
|
||||
url: 'https://device.example.com/complete',
|
||||
}]);
|
||||
assert.deepEqual(registerCalls, [{
|
||||
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');
|
||||
assert.equal(finalState.kiroClientSecret, 'secret-001');
|
||||
assert.equal(finalState.kiroDeviceAuthorizationCode, 'device-code-001');
|
||||
assert.equal(finalState.kiroDeviceCode, 'ABCD-1234');
|
||||
assert.equal(finalState.kiroLoginUrl, 'https://device.example.com/complete');
|
||||
assert.equal(finalState.kiroAuthRegion, 'us-east-1');
|
||||
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');
|
||||
assert.equal(completeCalls[0].payload.kiroDeviceCode, 'ABCD-1234');
|
||||
assert.equal(completeCalls[0].payload.kiroLoginUrl, 'https://device.example.com/complete');
|
||||
});
|
||||
|
||||
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,
|
||||
method: options.method || 'GET',
|
||||
body: options.body ? JSON.parse(options.body) : null,
|
||||
});
|
||||
pollCount += 1;
|
||||
if (pollCount === 1) {
|
||||
return createResponse({
|
||||
ok: false,
|
||||
status: 400,
|
||||
json: { error: 'authorization_pending' },
|
||||
});
|
||||
}
|
||||
return createResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: {
|
||||
accessToken: 'access-001',
|
||||
refreshToken: 'refresh-001',
|
||||
expiresIn: 3600,
|
||||
},
|
||||
});
|
||||
},
|
||||
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);
|
||||
},
|
||||
sleepWithStop: async (ms) => {
|
||||
sleepCalls.push(ms);
|
||||
},
|
||||
throwIfStopped: () => {},
|
||||
waitForTabStableComplete: async () => {},
|
||||
});
|
||||
|
||||
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, {
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
grantType: 'urn:ietf:params:oauth:grant-type:device_code',
|
||||
deviceCode: 'device-code-001',
|
||||
});
|
||||
assert.deepEqual(sleepCalls, [5000]);
|
||||
|
||||
const finalState = mergeUpdates(stateUpdates);
|
||||
assert.equal(finalState.kiroAuthStatus, 'authorized');
|
||||
assert.equal(finalState.kiroRefreshToken, 'refresh-001');
|
||||
assert.equal(finalState.kiroAccessToken, 'access-001');
|
||||
assert.equal(finalState.kiroUploadStatus, 'ready_to_upload');
|
||||
|
||||
assert.equal(completeCalls.length, 1);
|
||||
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 () => {
|
||||
const api = loadKiroDeviceAuthApi();
|
||||
const fetchCalls = [];
|
||||
const stateUpdates = [];
|
||||
const completeCalls = [];
|
||||
|
||||
const executor = api.createKiroDeviceAuthExecutor({
|
||||
addLog: async () => {},
|
||||
completeNodeFromBackground: async (nodeId, payload) => {
|
||||
completeCalls.push({ nodeId, payload });
|
||||
},
|
||||
fetchImpl: async (url, options = {}) => {
|
||||
fetchCalls.push({
|
||||
url,
|
||||
method: options.method || 'GET',
|
||||
headers: options.headers || {},
|
||||
body: options.body ? JSON.parse(options.body) : null,
|
||||
});
|
||||
if (options.method === 'GET') {
|
||||
return createResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: { success: true },
|
||||
});
|
||||
}
|
||||
return createResponse({
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: {
|
||||
success: true,
|
||||
message: 'uploaded',
|
||||
credentialId: 321,
|
||||
email: 'aws-user@example.com',
|
||||
},
|
||||
});
|
||||
},
|
||||
getState: async () => ({
|
||||
kiroRefreshToken: 'refresh-001',
|
||||
kiroClientId: 'client-001',
|
||||
kiroClientSecret: 'secret-001',
|
||||
kiroAuthRegion: 'ap-southeast-1',
|
||||
kiroAuthorizedEmail: 'cached@example.com',
|
||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||
kiroRsKey: 'admin-key-001',
|
||||
ipProxyEnabled: true,
|
||||
ipProxyProtocol: 'socks5',
|
||||
ipProxyHost: '127.0.0.1',
|
||||
ipProxyPort: '1080',
|
||||
ipProxyUsername: 'proxy-user',
|
||||
ipProxyPassword: 'proxy-pass',
|
||||
}),
|
||||
setState: async (updates) => {
|
||||
stateUpdates.push(updates);
|
||||
},
|
||||
sleepWithStop: async () => {},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await executor.executeKiroUploadCredential({
|
||||
nodeId: 'kiro-upload-credential',
|
||||
kiroRefreshToken: 'refresh-001',
|
||||
kiroClientId: 'client-001',
|
||||
kiroClientSecret: 'secret-001',
|
||||
kiroAuthRegion: 'ap-southeast-1',
|
||||
kiroAuthorizedEmail: 'cached@example.com',
|
||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||
kiroRsKey: 'admin-key-001',
|
||||
ipProxyEnabled: true,
|
||||
ipProxyProtocol: 'socks5',
|
||||
ipProxyHost: '127.0.0.1',
|
||||
ipProxyPort: '1080',
|
||||
ipProxyUsername: 'proxy-user',
|
||||
ipProxyPassword: 'proxy-pass',
|
||||
});
|
||||
|
||||
assert.equal(fetchCalls.length, 2);
|
||||
assert.equal(fetchCalls[0].url, 'https://kiro.example.com/api/admin/credentials');
|
||||
assert.equal(fetchCalls[0].method, 'GET');
|
||||
assert.equal(fetchCalls[0].headers['x-api-key'], 'admin-key-001');
|
||||
|
||||
assert.equal(fetchCalls[1].url, 'https://kiro.example.com/api/admin/credentials');
|
||||
assert.equal(fetchCalls[1].method, 'POST');
|
||||
assert.equal(fetchCalls[1].headers['x-api-key'], 'admin-key-001');
|
||||
assert.deepEqual(fetchCalls[1].body, {
|
||||
refreshToken: 'refresh-001',
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
region: 'ap-southeast-1',
|
||||
email: 'cached@example.com',
|
||||
priority: 0,
|
||||
authMethod: 'IdC',
|
||||
provider: 'BuilderId',
|
||||
proxyUrl: 'socks5://127.0.0.1:1080',
|
||||
proxyUsername: 'proxy-user',
|
||||
proxyPassword: 'proxy-pass',
|
||||
});
|
||||
|
||||
const finalState = mergeUpdates(stateUpdates);
|
||||
assert.equal(finalState.kiroLastConnectionMessage, 'kiro.rs 连接正常(HTTP 200)');
|
||||
assert.equal(finalState.kiroAuthorizedEmail, 'aws-user@example.com');
|
||||
assert.equal(finalState.kiroCredentialId, 321);
|
||||
assert.equal(finalState.kiroUploadStatus, '上传成功');
|
||||
assert.equal(typeof finalState.kiroLastUploadAt, 'number');
|
||||
assert.equal(finalState.kiroLastUploadAt > 0, true);
|
||||
|
||||
assert.equal(completeCalls.length, 1);
|
||||
assert.equal(completeCalls[0].nodeId, 'kiro-upload-credential');
|
||||
assert.equal(completeCalls[0].payload.kiroCredentialId, 321);
|
||||
assert.equal(completeCalls[0].payload.kiroUploadStatus, '上传成功');
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadPublisherApi() {
|
||||
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
|
||||
const publisherSource = fs.readFileSync('background/kiro/publisher-kiro-rs.js', 'utf8');
|
||||
const globalScope = {};
|
||||
new Function('self', `${stateSource}; ${publisherSource}; return self;`)(globalScope);
|
||||
return globalScope.MultiPageBackgroundKiroPublisherKiroRs;
|
||||
}
|
||||
|
||||
test('kiro publisher exposes a factory and upload payload helpers', () => {
|
||||
const api = loadPublisherApi();
|
||||
assert.equal(typeof api?.createKiroRsPublisher, 'function');
|
||||
assert.equal(typeof api?.buildKiroRsPayload, 'function');
|
||||
assert.equal(typeof api?.buildMachineId, 'function');
|
||||
});
|
||||
|
||||
test('kiro publisher builds kiro.rs payload from desktop auth runtime without profileArn', async () => {
|
||||
const api = loadPublisherApi();
|
||||
const payload = api.buildKiroRsPayload({
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||
kiroRsKey: 'demo-key',
|
||||
ipProxyEnabled: true,
|
||||
ipProxyHost: '1.2.3.4',
|
||||
ipProxyPort: '8080',
|
||||
ipProxyProtocol: 'http',
|
||||
ipProxyUsername: 'proxy-user',
|
||||
ipProxyPassword: 'proxy-pass',
|
||||
kiroRuntime: {
|
||||
register: {
|
||||
email: 'aws-user@example.com',
|
||||
},
|
||||
desktopAuth: {
|
||||
region: 'us-east-1',
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
refreshToken: 'refresh-token-001',
|
||||
},
|
||||
upload: {
|
||||
targetId: 'kiro-rs',
|
||||
},
|
||||
},
|
||||
});
|
||||
const machineId = await api.buildMachineId('refresh-token-001');
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
targetId: 'kiro-rs',
|
||||
region: 'us-east-1',
|
||||
email: 'aws-user@example.com',
|
||||
refreshToken: 'refresh-token-001',
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
authMethod: 'idc',
|
||||
authRegion: 'us-east-1',
|
||||
apiRegion: 'us-east-1',
|
||||
proxyUrl: 'http://1.2.3.4:8080',
|
||||
proxyUsername: 'proxy-user',
|
||||
proxyPassword: 'proxy-pass',
|
||||
});
|
||||
assert.equal(machineId.length, 64);
|
||||
assert.match(machineId, /^[0-9a-f]{64}$/);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'profileArn'), false);
|
||||
});
|
||||
@@ -0,0 +1,59 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadRegisterRunnerApi() {
|
||||
const stateSource = fs.readFileSync('background/kiro/state.js', 'utf8');
|
||||
const runnerSource = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
|
||||
const globalScope = {};
|
||||
new Function('self', `${stateSource}; ${runnerSource}; return self;`)(globalScope);
|
||||
return globalScope.MultiPageBackgroundKiroRegisterRunner;
|
||||
}
|
||||
|
||||
test('kiro register runner module exposes a factory and device login bootstrap helper', () => {
|
||||
const api = loadRegisterRunnerApi();
|
||||
assert.equal(typeof api?.createKiroRegisterRunner, 'function');
|
||||
assert.equal(typeof api?.startBuilderIdDeviceLogin, 'function');
|
||||
});
|
||||
|
||||
test('startBuilderIdDeviceLogin registers Builder ID client and returns login bootstrap payload', async () => {
|
||||
const api = loadRegisterRunnerApi();
|
||||
const requests = [];
|
||||
const result = await api.startBuilderIdDeviceLogin('us-east-1', async (url, options = {}) => {
|
||||
requests.push({ url, options });
|
||||
if (url.endsWith('/client/register')) {
|
||||
return {
|
||||
ok: true,
|
||||
text: async () => JSON.stringify({
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
}),
|
||||
};
|
||||
}
|
||||
if (url.endsWith('/device_authorization')) {
|
||||
return {
|
||||
ok: true,
|
||||
text: async () => JSON.stringify({
|
||||
deviceCode: 'device-code-001',
|
||||
userCode: 'ABCD-1234',
|
||||
verificationUri: 'https://view.awsapps.com/start',
|
||||
verificationUriComplete: 'https://view.awsapps.com/start?user_code=ABCD-1234',
|
||||
interval: 7,
|
||||
expiresIn: 600,
|
||||
}),
|
||||
};
|
||||
}
|
||||
throw new Error(`Unexpected request: ${url}`);
|
||||
});
|
||||
|
||||
assert.equal(requests.length, 2);
|
||||
assert.equal(requests[0].url, 'https://oidc.us-east-1.amazonaws.com/client/register');
|
||||
assert.equal(requests[1].url, 'https://oidc.us-east-1.amazonaws.com/device_authorization');
|
||||
assert.equal(result.clientId, 'client-001');
|
||||
assert.equal(result.clientSecret, 'secret-001');
|
||||
assert.equal(result.deviceCode, 'device-code-001');
|
||||
assert.equal(result.userCode, 'ABCD-1234');
|
||||
assert.equal(result.verificationUriComplete, 'https://view.awsapps.com/start?user_code=ABCD-1234');
|
||||
assert.equal(result.interval, 7);
|
||||
assert.equal(result.region, 'us-east-1');
|
||||
});
|
||||
@@ -0,0 +1,141 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadKiroStateApi() {
|
||||
const source = fs.readFileSync('background/kiro/state.js', 'utf8');
|
||||
const globalScope = {};
|
||||
return new Function('self', `${source}; return self.MultiPageBackgroundKiroState;`)(globalScope);
|
||||
}
|
||||
|
||||
test('background imports kiro state module and routes Kiro runtime through dedicated helpers', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
assert.match(source, /background\/kiro\/state\.js/);
|
||||
assert.match(source, /const kiroStateHelpers = self\.MultiPageBackgroundKiroState/);
|
||||
assert.match(source, /kiroStateHelpers\?\.buildStateView/);
|
||||
assert.match(source, /kiroStateHelpers\?\.buildSessionStatePatch/);
|
||||
assert.match(source, /kiroStateHelpers\?\.buildDownstreamResetPatch/);
|
||||
assert.match(source, /kiroStateHelpers\?\.applyNodeCompletionPayload/);
|
||||
});
|
||||
|
||||
test('kiro state module exposes canonical nested kiroRuntime view', () => {
|
||||
const api = loadKiroStateApi();
|
||||
const view = api.buildStateView({
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRuntime: {
|
||||
session: {
|
||||
currentStage: 'desktop-authorize',
|
||||
registerTabId: 88,
|
||||
pageState: 'name_entry',
|
||||
pageUrl: 'https://view.awsapps.com/start',
|
||||
},
|
||||
register: {
|
||||
email: 'aws-user@example.com',
|
||||
fullName: 'Ada Lovelace',
|
||||
userCode: 'ABCD-1234',
|
||||
loginUrl: 'https://device.example.com/complete',
|
||||
status: 'waiting_name',
|
||||
},
|
||||
desktopAuth: {
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
refreshToken: 'refresh-001',
|
||||
status: 'authorized',
|
||||
},
|
||||
upload: {
|
||||
targetId: 'kiro-rs',
|
||||
status: 'ready_to_upload',
|
||||
credentialId: 321,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(view.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(view.kiroRuntime.session.currentStage, 'desktop-authorize');
|
||||
assert.equal(view.kiroRuntime.session.registerTabId, 88);
|
||||
assert.equal(view.kiroRuntime.register.email, 'aws-user@example.com');
|
||||
assert.equal(view.kiroRuntime.register.userCode, 'ABCD-1234');
|
||||
assert.equal(view.kiroRuntime.desktopAuth.clientId, 'client-001');
|
||||
assert.equal(view.kiroRuntime.desktopAuth.refreshToken, 'refresh-001');
|
||||
assert.equal(view.kiroRuntime.upload.status, 'ready_to_upload');
|
||||
assert.equal(view.kiroRuntime.upload.credentialId, 321);
|
||||
});
|
||||
|
||||
test('kiro state session patch accepts canonical nested runtime updates', () => {
|
||||
const api = loadKiroStateApi();
|
||||
const patch = api.buildSessionStatePatch({
|
||||
kiroRuntime: api.buildDefaultRuntimeState(),
|
||||
}, {
|
||||
kiroRuntime: {
|
||||
session: {
|
||||
currentStage: 'register',
|
||||
pageState: 'otp_page',
|
||||
pageUrl: 'https://signin.aws/register',
|
||||
},
|
||||
register: {
|
||||
email: 'aws-user@example.com',
|
||||
fullName: 'Ada Lovelace',
|
||||
verificationRequestedAt: 1700000000000,
|
||||
},
|
||||
desktopAuth: {
|
||||
status: 'waiting_callback',
|
||||
},
|
||||
upload: {
|
||||
status: 'waiting_register',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(patch.kiroRuntime.session.currentStage, 'register');
|
||||
assert.equal(patch.kiroRuntime.session.pageState, 'otp_page');
|
||||
assert.equal(patch.kiroRuntime.session.pageUrl, 'https://signin.aws/register');
|
||||
assert.equal(patch.kiroRuntime.register.email, 'aws-user@example.com');
|
||||
assert.equal(patch.kiroRuntime.register.fullName, 'Ada Lovelace');
|
||||
assert.equal(patch.kiroRuntime.register.verificationRequestedAt, 1700000000000);
|
||||
assert.equal(patch.kiroRuntime.desktopAuth.status, 'waiting_callback');
|
||||
assert.equal(patch.kiroRuntime.upload.status, 'waiting_register');
|
||||
});
|
||||
|
||||
test('kiro state reset helpers clear downstream runtime and fresh keep-state preserves only target selection', () => {
|
||||
const api = loadKiroStateApi();
|
||||
const currentState = {
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRuntime: {
|
||||
session: {
|
||||
currentStage: 'upload',
|
||||
registerTabId: 88,
|
||||
},
|
||||
register: {
|
||||
email: 'aws-user@example.com',
|
||||
fullName: 'Ada Lovelace',
|
||||
status: 'completed',
|
||||
},
|
||||
desktopAuth: {
|
||||
clientId: 'client-001',
|
||||
clientSecret: 'secret-001',
|
||||
refreshToken: 'refresh-001',
|
||||
status: 'authorized',
|
||||
},
|
||||
upload: {
|
||||
targetId: 'kiro-rs',
|
||||
status: 'uploaded',
|
||||
credentialId: 321,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const resetPatch = api.buildDownstreamResetPatch('kiro-submit-email', currentState);
|
||||
assert.equal(resetPatch.kiroRuntime.session.currentStage, 'register');
|
||||
assert.equal(resetPatch.kiroRuntime.register.email, '');
|
||||
assert.equal(resetPatch.kiroRuntime.register.fullName, '');
|
||||
assert.equal(resetPatch.kiroRuntime.desktopAuth.refreshToken, '');
|
||||
assert.equal(resetPatch.kiroRuntime.upload.status, '');
|
||||
assert.equal(resetPatch.kiroRuntime.upload.credentialId, null);
|
||||
|
||||
const keepState = api.buildFreshKeepState(currentState);
|
||||
assert.equal(keepState.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(keepState.kiroRuntime.register.email, '');
|
||||
assert.equal(keepState.kiroRuntime.desktopAuth.refreshToken, '');
|
||||
assert.equal(keepState.kiroRuntime.upload.status, '');
|
||||
assert.equal(keepState.kiroRuntime.upload.targetId, 'kiro-rs');
|
||||
});
|
||||
@@ -361,7 +361,7 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
|
||||
validations.push({
|
||||
activeFlowId: validationState?.activeFlowId,
|
||||
flowId: validationState?.flowId,
|
||||
kiroSourceId: validationState?.kiroSourceId,
|
||||
kiroTargetId: validationState?.kiroTargetId,
|
||||
optionActiveFlowId: options?.activeFlowId,
|
||||
});
|
||||
return { ok: true, errors: [] };
|
||||
@@ -373,21 +373,21 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
|
||||
payload: {
|
||||
totalRuns: 1,
|
||||
activeFlowId: 'kiro',
|
||||
sourceId: 'kiro-rs',
|
||||
targetId: 'kiro-rs',
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(response.ok, true);
|
||||
assert.equal(state.activeFlowId, 'kiro');
|
||||
assert.equal(state.flowId, 'kiro');
|
||||
assert.equal(state.kiroSourceId, 'kiro-rs');
|
||||
assert.equal(state.kiroTargetId, 'kiro-rs');
|
||||
assert.deepStrictEqual(calls, [
|
||||
{
|
||||
type: 'setState',
|
||||
updates: {
|
||||
activeFlowId: 'kiro',
|
||||
flowId: 'kiro',
|
||||
kiroSourceId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -409,7 +409,7 @@ test('AUTO_RUN applies current flow selection from payload before starting loop'
|
||||
{
|
||||
activeFlowId: 'kiro',
|
||||
flowId: 'kiro',
|
||||
kiroSourceId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
optionActiveFlowId: 'kiro',
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -147,7 +147,7 @@ test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId
|
||||
DEFAULT_ACTIVE_FLOW_ID: 'openai',
|
||||
defaultNodeStatuses: {
|
||||
'open-chatgpt': 'pending',
|
||||
'kiro-start-device-login': 'pending',
|
||||
'kiro-open-register-page': 'pending',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId
|
||||
}, {
|
||||
activeFlowId: 'kiro',
|
||||
nodeStatuses: {
|
||||
'kiro-start-device-login': 'running',
|
||||
'kiro-open-register-page': 'running',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -168,6 +168,6 @@ test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId
|
||||
assert.equal(patch.flowId, 'kiro');
|
||||
assert.deepStrictEqual(patch.nodeStatuses, {
|
||||
'open-chatgpt': 'pending',
|
||||
'kiro-start-device-login': 'running',
|
||||
'kiro-open-register-page': 'running',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -60,9 +60,8 @@ const DEFAULT_SUB2API_GROUP_NAMES = ['codex', 'openai-plus'];
|
||||
const SETTINGS_SCHEMA_VIEW_KEYS = Object.freeze([
|
||||
'activeFlowId',
|
||||
'openaiIntegrationTargetId',
|
||||
'kiroIntegrationTargetId',
|
||||
'panelMode',
|
||||
'kiroSourceId',
|
||||
'kiroTargetId',
|
||||
'vpsUrl',
|
||||
'vpsPassword',
|
||||
'localCpaStep9Mode',
|
||||
@@ -101,7 +100,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
ipProxyEnabled: false,
|
||||
ipProxyService: '711proxy',
|
||||
ipProxyMode: 'account',
|
||||
kiroSourceId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRsUrl: 'https://kiro.leftcode.xyz/admin',
|
||||
kiroRsKey: '',
|
||||
stepExecutionRangeByFlow: {},
|
||||
@@ -194,15 +193,15 @@ test('buildPersistentSettingsPayload writes canonical settings schema into persi
|
||||
}, { fillDefaults: true });
|
||||
|
||||
assert.equal(payload.activeFlowId, 'kiro');
|
||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
||||
assert.equal(payload.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(payload.kiroRsKey, 'secret-key');
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||
assert.equal(payload.settingsSchemaVersion, 4);
|
||||
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
||||
assert.equal(payload.settingsState.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||
assert.equal(payload.settingsState.flows.kiro.targetId, 'kiro-rs');
|
||||
assert.equal(
|
||||
payload.settingsState.flows.kiro.integrationTargets['kiro-rs'].baseUrl,
|
||||
payload.settingsState.flows.kiro.targets['kiro-rs'].baseUrl,
|
||||
'https://kiro.example.com/admin'
|
||||
);
|
||||
});
|
||||
@@ -256,15 +255,15 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
||||
},
|
||||
},
|
||||
kiro: {
|
||||
integrationTargetId: 'kiro-rs',
|
||||
integrationTargets: {
|
||||
targetId: 'kiro-rs',
|
||||
targets: {
|
||||
'kiro-rs': {
|
||||
baseUrl: 'https://kiro.example.com/admin',
|
||||
apiKey: 'schema-only-key',
|
||||
},
|
||||
},
|
||||
autoRun: {
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -272,7 +271,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
||||
}, { requireKnownKeys: true });
|
||||
|
||||
assert.equal(payload.activeFlowId, 'kiro');
|
||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
||||
assert.equal(payload.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||
@@ -357,15 +356,15 @@ const chrome = {
|
||||
},
|
||||
},
|
||||
kiro: {
|
||||
integrationTargetId: 'kiro-rs',
|
||||
integrationTargets: {
|
||||
targetId: 'kiro-rs',
|
||||
targets: {
|
||||
'kiro-rs': {
|
||||
baseUrl: 'https://kiro.example.com/admin',
|
||||
apiKey: 'stored-key',
|
||||
},
|
||||
},
|
||||
autoRun: {
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -389,7 +388,7 @@ const chrome = {
|
||||
assert.deepEqual(state.stepExecutionRangeByFlow.kiro, {
|
||||
enabled: true,
|
||||
fromStep: 1,
|
||||
toStep: 7,
|
||||
toStep: 9,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -466,15 +465,15 @@ function getRemovedKeys() {
|
||||
},
|
||||
},
|
||||
kiro: {
|
||||
integrationTargetId: 'kiro-rs',
|
||||
integrationTargets: {
|
||||
targetId: 'kiro-rs',
|
||||
targets: {
|
||||
'kiro-rs': {
|
||||
baseUrl: 'https://kiro.example.com/admin',
|
||||
apiKey: 'nested-only-key',
|
||||
},
|
||||
},
|
||||
autoRun: {
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -484,6 +483,7 @@ function getRemovedKeys() {
|
||||
const write = api.getPersistedWrites().at(-1);
|
||||
|
||||
assert.equal(persisted.activeFlowId, 'kiro');
|
||||
assert.equal(persisted.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
|
||||
@@ -494,7 +494,7 @@ function getRemovedKeys() {
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
|
||||
assert.equal(write.settingsSchemaVersion, 4);
|
||||
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
||||
assert.equal(write.settingsState.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||
assert.equal(write.settingsState.flows.kiro.targetId, 'kiro-rs');
|
||||
assert.ok(api.getRemovedKeys().includes('panelMode'));
|
||||
assert.ok(api.getRemovedKeys().includes('kiroRsUrl'));
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
test('background imports workflow step modules including Kiro device auth', () => {
|
||||
test('background imports workflow step modules including rebuilt Kiro modules', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
|
||||
[
|
||||
@@ -16,8 +16,14 @@ test('background imports workflow step modules including Kiro device auth', () =
|
||||
'background/steps/fetch-login-code.js',
|
||||
'background/steps/confirm-oauth.js',
|
||||
'background/steps/platform-verify.js',
|
||||
'background/steps/kiro-device-auth.js',
|
||||
'background/kiro/state.js',
|
||||
'background/kiro/register-runner.js',
|
||||
'background/kiro/desktop-client.js',
|
||||
'background/kiro/desktop-authorize-runner.js',
|
||||
'background/kiro/publisher-kiro-rs.js',
|
||||
].forEach((path) => {
|
||||
assert.match(source, new RegExp(path.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')));
|
||||
});
|
||||
|
||||
assert.doesNotMatch(source, /background\/steps\/kiro-device-auth\.js/);
|
||||
});
|
||||
|
||||
@@ -8,21 +8,21 @@ test('background node registry preserves node metadata even before an executor i
|
||||
const registry = api.createNodeRegistry([
|
||||
{
|
||||
flowId: 'kiro',
|
||||
nodeId: 'kiro-start-device-login',
|
||||
nodeId: 'kiro-open-register-page',
|
||||
displayOrder: 1,
|
||||
executeKey: 'kiro-start-device-login',
|
||||
title: 'Start device login',
|
||||
executeKey: 'kiro-open-register-page',
|
||||
title: '打开注册页',
|
||||
},
|
||||
]);
|
||||
|
||||
const node = registry.getNodeDefinition('kiro-start-device-login');
|
||||
const node = registry.getNodeDefinition('kiro-open-register-page');
|
||||
|
||||
assert.equal(node.flowId, 'kiro');
|
||||
assert.equal(node.displayOrder, 1);
|
||||
assert.equal(node.title, 'Start device login');
|
||||
assert.equal(node.title, '打开注册页');
|
||||
assert.throws(
|
||||
() => registry.executeNode('kiro-start-device-login', {}),
|
||||
/Missing node executor: kiro-start-device-login/
|
||||
() => registry.executeNode('kiro-open-register-page', {}),
|
||||
/Missing node executor: kiro-open-register-page/
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
test('background imports node registry and shared workflow definitions', () => {
|
||||
test('background imports node registry and wires the rebuilt Kiro executors', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
assert.match(source, /background\/steps\/registry\.js/);
|
||||
assert.match(source, /data\/step-definitions\.js/);
|
||||
@@ -12,28 +12,31 @@ test('background imports node registry and shared workflow definitions', () => {
|
||||
assert.match(source, /const stepRegistryCache = new Map\(\);/);
|
||||
assert.match(source, /const definitions = getNodeDefinitionsForState\(state\);/);
|
||||
assert.match(source, /stepRegistryCache\.set\(cacheKey, buildStepRegistry\(definitions\)\)/);
|
||||
assert.match(source, /'bind-email': \(state\) => step8Executor\.executeBindEmail\(state\)/);
|
||||
assert.match(source, /'fetch-bind-email-code': \(state\) => step8Executor\.executeFetchBindEmailCode\(state\)/);
|
||||
assert.match(source, /'relogin-bound-email': \(state\) => executeReloginBoundEmail\(state\)/);
|
||||
assert.match(source, /'fetch-bound-email-login-code': \(state\) => step8Executor\.executeBoundEmailLoginCode\(state\)/);
|
||||
assert.match(source, /'post-bound-email-phone-verification': \(state\) => step8Executor\.executeBoundEmailPostLoginPhoneVerification\(state\)/);
|
||||
assert.match(source, /background\/steps\/create-plus-checkout\.js/);
|
||||
assert.match(source, /background\/steps\/fill-plus-checkout\.js/);
|
||||
assert.match(source, /background\/steps\/gopay-manual-confirm\.js/);
|
||||
assert.match(source, /'gopay-subscription-confirm': \(state\) => goPayManualConfirmExecutor\.executeGoPayManualConfirm\(state\)/);
|
||||
assert.match(source, /background\/steps\/paypal-approve\.js/);
|
||||
assert.match(source, /background\/steps\/gopay-approve\.js/);
|
||||
assert.match(source, /background\/steps\/plus-return-confirm\.js/);
|
||||
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-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-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'/);
|
||||
|
||||
assert.match(source, /background\/kiro\/register-runner\.js/);
|
||||
assert.match(source, /background\/kiro\/desktop-client\.js/);
|
||||
assert.match(source, /background\/kiro\/desktop-authorize-runner\.js/);
|
||||
assert.match(source, /background\/kiro\/publisher-kiro-rs\.js/);
|
||||
assert.doesNotMatch(source, /background\/steps\/kiro-device-auth\.js/);
|
||||
|
||||
assert.match(source, /const kiroRegisterRunner = self\.MultiPageBackgroundKiroRegisterRunner\?\.createKiroRegisterRunner\(/);
|
||||
assert.match(source, /const kiroDesktopAuthorizeRunner = self\.MultiPageBackgroundKiroDesktopAuthorizeRunner\?\.createKiroDesktopAuthorizeRunner\(/);
|
||||
assert.match(source, /const kiroPublisher = self\.MultiPageBackgroundKiroPublisherKiroRs\?\.createKiroRsPublisher\(/);
|
||||
|
||||
assert.match(source, /'kiro-open-register-page': \(state\) => kiroRegisterRunner\.executeKiroOpenRegisterPage\(state\)/);
|
||||
assert.match(source, /'kiro-submit-email': \(state\) => kiroRegisterRunner\.executeKiroSubmitEmail\(state\)/);
|
||||
assert.match(source, /'kiro-submit-name': \(state\) => kiroRegisterRunner\.executeKiroSubmitName\(state\)/);
|
||||
assert.match(source, /'kiro-submit-verification-code': \(state\) => kiroRegisterRunner\.executeKiroSubmitVerificationCode\(state\)/);
|
||||
assert.match(source, /'kiro-submit-password': \(state\) => kiroRegisterRunner\.executeKiroSubmitPassword\(state\)/);
|
||||
assert.match(source, /'kiro-complete-register-consent': \(state\) => kiroRegisterRunner\.executeKiroCompleteRegisterConsent\(state\)/);
|
||||
assert.match(source, /'kiro-start-desktop-authorize': \(state\) => kiroDesktopAuthorizeRunner\.executeKiroStartDesktopAuthorize\(state\)/);
|
||||
assert.match(source, /'kiro-complete-desktop-authorize': \(state\) => kiroDesktopAuthorizeRunner\.executeKiroCompleteDesktopAuthorize\(state\)/);
|
||||
assert.match(source, /'kiro-upload-credential': \(state\) => kiroPublisher\.executeKiroUploadCredential\(state\)/);
|
||||
|
||||
assert.match(
|
||||
source,
|
||||
/'kiro-open-register-page',[\s\S]*'kiro-submit-email',[\s\S]*'kiro-submit-name',[\s\S]*'kiro-submit-verification-code',[\s\S]*'kiro-submit-password',[\s\S]*'kiro-complete-register-consent',[\s\S]*'kiro-start-desktop-authorize',[\s\S]*'kiro-complete-desktop-authorize',[\s\S]*'kiro-upload-credential'/
|
||||
);
|
||||
});
|
||||
|
||||
test('GoPay approve executor receives debugger click and manual OTP helpers', () => {
|
||||
|
||||
@@ -178,7 +178,7 @@ test('tab runtime replays retryable transport recovery hook and surfaces a local
|
||||
getSourceLabel: () => 'Kiro 授权页',
|
||||
getState: async () => ({
|
||||
tabRegistry: {
|
||||
'kiro-device-auth': { tabId: 9, ready: true },
|
||||
'kiro-register-page': { tabId: 9, ready: true },
|
||||
},
|
||||
sourceLastUrls: {},
|
||||
}),
|
||||
@@ -190,7 +190,7 @@ test('tab runtime replays retryable transport recovery hook and surfaces a local
|
||||
});
|
||||
|
||||
await assert.rejects(
|
||||
runtime.sendToContentScriptResilient('kiro-device-auth', {
|
||||
runtime.sendToContentScriptResilient('kiro-register-page', {
|
||||
type: 'ENSURE_KIRO_PAGE_STATE',
|
||||
payload: {},
|
||||
}, {
|
||||
|
||||
@@ -73,7 +73,7 @@ test('flow capability registry defaults unknown flows to minimal non-phone capab
|
||||
assert.equal(capabilityState.canUsePhoneSignup, false);
|
||||
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
||||
assert.equal(capabilityState.panelMode, 'codex2api');
|
||||
assert.deepEqual(capabilityState.supportedIntegrationTargets, []);
|
||||
assert.deepEqual(capabilityState.supportedTargetIds, []);
|
||||
});
|
||||
|
||||
test('flow capability registry exposes Kiro as an independent flow with its own visible groups', () => {
|
||||
@@ -83,7 +83,7 @@ test('flow capability registry exposes Kiro as an independent flow with its own
|
||||
const capabilityState = registry.resolveSidepanelCapabilities({
|
||||
state: {
|
||||
activeFlowId: 'kiro',
|
||||
kiroIntegrationTargetId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
openaiIntegrationTargetId: 'sub2api',
|
||||
signupMethod: 'phone',
|
||||
plusModeEnabled: true,
|
||||
@@ -95,7 +95,7 @@ test('flow capability registry exposes Kiro as an independent flow with its own
|
||||
assert.equal(capabilityState.canShowPhoneSettings, false);
|
||||
assert.equal(capabilityState.canShowPlusSettings, false);
|
||||
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
||||
assert.equal(capabilityState.effectiveIntegrationTargetId, 'kiro-rs');
|
||||
assert.equal(capabilityState.effectiveTargetId, 'kiro-rs');
|
||||
assert.deepEqual(
|
||||
capabilityState.visibleGroupIds,
|
||||
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
||||
@@ -109,7 +109,7 @@ test('flow capability registry exposes shared auto-run validation for phone lock
|
||||
openai: api.FLOW_CAPABILITIES.openai,
|
||||
'site-a': {
|
||||
...api.DEFAULT_FLOW_CAPABILITIES,
|
||||
supportedIntegrationTargets: ['cpa'],
|
||||
supportedTargetIds: ['cpa'],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -147,7 +147,7 @@ test('flow capability registry normalizes unsupported mode switches back to the
|
||||
openai: api.FLOW_CAPABILITIES.openai,
|
||||
'site-a': {
|
||||
...api.DEFAULT_FLOW_CAPABILITIES,
|
||||
supportedIntegrationTargets: ['cpa'],
|
||||
supportedTargetIds: ['cpa'],
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -174,8 +174,7 @@ test('flow capability registry normalizes unsupported mode switches back to the
|
||||
assert.deepEqual(validation.normalizedUpdates, {
|
||||
panelMode: 'cpa',
|
||||
openaiIntegrationTargetId: 'cpa',
|
||||
kiroIntegrationTargetId: 'cpa',
|
||||
kiroSourceId: 'cpa',
|
||||
kiroTargetId: 'cpa',
|
||||
signupMethod: 'email',
|
||||
phoneVerificationEnabled: false,
|
||||
plusModeEnabled: false,
|
||||
|
||||
@@ -13,15 +13,15 @@ function loadApis() {
|
||||
};`)(scope);
|
||||
}
|
||||
|
||||
test('flow registry exposes canonical flow and integration target metadata', () => {
|
||||
test('flow registry exposes canonical flow and target metadata', () => {
|
||||
const { flowRegistry } = loadApis();
|
||||
|
||||
assert.deepEqual(flowRegistry.getRegisteredFlowIds(), ['openai', 'kiro']);
|
||||
assert.equal(flowRegistry.normalizeFlowId('kiro'), 'kiro');
|
||||
assert.equal(flowRegistry.normalizeFlowId('unknown'), 'openai');
|
||||
assert.equal(flowRegistry.getFlowLabel('openai'), 'Codex / OpenAI');
|
||||
assert.equal(flowRegistry.normalizeIntegrationTargetId('openai', 'sub2api'), 'sub2api');
|
||||
assert.equal(flowRegistry.normalizeIntegrationTargetId('kiro', 'anything-else'), 'kiro-rs');
|
||||
assert.equal(flowRegistry.normalizeTargetId('openai', 'sub2api'), 'sub2api');
|
||||
assert.equal(flowRegistry.normalizeTargetId('kiro', 'anything-else'), 'kiro-rs');
|
||||
assert.deepEqual(
|
||||
flowRegistry.getVisibleGroupIds('openai', 'cpa'),
|
||||
['openai-plus', 'openai-phone', 'openai-oauth', 'openai-step6', 'openai-target-cpa', 'service-account', 'service-email', 'service-proxy']
|
||||
@@ -31,7 +31,7 @@ test('flow registry exposes canonical flow and integration target metadata', ()
|
||||
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
||||
);
|
||||
assert.deepEqual(
|
||||
flowRegistry.getIntegrationTargetOptions('openai').map((entry) => entry.id),
|
||||
flowRegistry.getTargetOptions('openai').map((entry) => entry.id),
|
||||
['cpa', 'sub2api', 'codex2api']
|
||||
);
|
||||
assert.equal(flowRegistry.getPublicationTargetDefinition('kiro', 'kiro-rs')?.label, 'kiro.rs');
|
||||
@@ -52,7 +52,7 @@ test('settings schema normalizes view input into canonical nested namespaces', (
|
||||
kiroRsKey: 'secret-key',
|
||||
stepExecutionRangeByFlow: {
|
||||
openai: { enabled: true, fromStep: 2, toStep: 9 },
|
||||
kiro: { enabled: true, fromStep: 1, toStep: 7 },
|
||||
kiro: { enabled: true, fromStep: 1, toStep: 9 },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -61,13 +61,13 @@ test('settings schema normalizes view input into canonical nested namespaces', (
|
||||
assert.equal(normalized.services.proxy.enabled, true);
|
||||
assert.equal(normalized.services.account.customPassword, 'SharedSecret123!');
|
||||
assert.equal(normalized.flows.openai.integrationTargetId, 'sub2api');
|
||||
assert.equal(normalized.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||
assert.equal(normalized.flows.kiro.integrationTargets['kiro-rs'].baseUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(normalized.flows.kiro.integrationTargets['kiro-rs'].apiKey, 'secret-key');
|
||||
assert.equal(normalized.flows.kiro.targetId, 'kiro-rs');
|
||||
assert.equal(normalized.flows.kiro.targets['kiro-rs'].baseUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(normalized.flows.kiro.targets['kiro-rs'].apiKey, 'secret-key');
|
||||
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
|
||||
enabled: true,
|
||||
fromStep: 1,
|
||||
toStep: 7,
|
||||
toStep: 9,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -76,7 +76,7 @@ test('settings schema can project canonical state into a read view without legac
|
||||
const schema = settingsSchema.createSettingsSchema();
|
||||
const normalized = schema.normalizeSettingsState({
|
||||
activeFlowId: 'kiro',
|
||||
kiroIntegrationTargetId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||
kiroRsKey: 'key-123',
|
||||
});
|
||||
@@ -84,9 +84,8 @@ test('settings schema can project canonical state into a read view without legac
|
||||
|
||||
assert.equal(view.activeFlowId, 'kiro');
|
||||
assert.equal(view.openaiIntegrationTargetId, 'cpa');
|
||||
assert.equal(view.kiroIntegrationTargetId, 'kiro-rs');
|
||||
assert.equal(view.kiroTargetId, 'kiro-rs');
|
||||
assert.equal(view.panelMode, 'cpa');
|
||||
assert.equal(view.kiroSourceId, 'kiro-rs');
|
||||
assert.equal(view.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||
assert.equal(view.kiroRsKey, 'key-123');
|
||||
assert.equal(view.settingsSchemaVersion, 4);
|
||||
|
||||
@@ -68,7 +68,7 @@ const latestState = {
|
||||
activeFlowId: 'openai',
|
||||
flowId: 'openai',
|
||||
panelMode: 'cpa',
|
||||
kiroSourceId: 'kiro-rs',
|
||||
kiroTargetId: 'kiro-rs',
|
||||
};
|
||||
const inputAutoSkipFailures = { checked: false };
|
||||
const inputContributionNickname = { value: 'tester' };
|
||||
@@ -118,10 +118,10 @@ function normalizePanelMode(value = '', fallback = '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()) {
|
||||
function getSelectedTargetId(flowId = getSelectedFlowId()) {
|
||||
return String(
|
||||
flowId === 'kiro'
|
||||
? (selectPanelMode.value || latestState.kiroSourceId || 'kiro-rs')
|
||||
? (selectPanelMode.value || latestState.kiroTargetId || 'kiro-rs')
|
||||
: normalizePanelMode(selectPanelMode.value || latestState.panelMode || 'cpa')
|
||||
).trim().toLowerCase() || (flowId === 'kiro' ? 'kiro-rs' : 'cpa');
|
||||
}
|
||||
@@ -225,7 +225,7 @@ test('startAutoRunFromCurrentSettings sends current flow selection with auto run
|
||||
selectPanelMode.value = 'kiro-rs';
|
||||
latestState.activeFlowId = 'openai';
|
||||
latestState.flowId = 'openai';
|
||||
latestState.kiroSourceId = 'kiro-rs';
|
||||
latestState.kiroTargetId = 'kiro-rs';
|
||||
events.push({ type: 'flow-switch-race' });
|
||||
}`,
|
||||
});
|
||||
@@ -235,7 +235,7 @@ test('startAutoRunFromCurrentSettings sends current flow selection with auto run
|
||||
|
||||
assert.equal(result, true);
|
||||
assert.equal(sendEvent.message.payload.activeFlowId, 'kiro');
|
||||
assert.equal(sendEvent.message.payload.sourceId, 'kiro-rs');
|
||||
assert.equal(sendEvent.message.payload.targetId, 'kiro-rs');
|
||||
});
|
||||
|
||||
test('startAutoRunFromCurrentSettings blocks when shared flow capability validation fails', async () => {
|
||||
|
||||
@@ -184,14 +184,14 @@ function normalizePanelMode(value = '', fallback = 'cpa') {
|
||||
function getSelectedFlowId() {
|
||||
return latestState.activeFlowId;
|
||||
}
|
||||
function getSelectedSourceId() {
|
||||
function getSelectedTargetId() {
|
||||
return 'cpa';
|
||||
}
|
||||
function renderFlowSelectorOptions(flowId) {
|
||||
calls.push({ type: 'render-flow', flowId });
|
||||
}
|
||||
function renderSourceSelectorOptions(flowId, sourceId) {
|
||||
calls.push({ type: 'render-source', flowId, sourceId });
|
||||
function renderTargetSelectorOptions(flowId, targetId) {
|
||||
calls.push({ type: 'render-target', flowId, targetId });
|
||||
}
|
||||
function applyFlowSettingsGroupVisibility(visibleGroupIds) {
|
||||
calls.push({ type: 'groups', visibleGroupIds: [...visibleGroupIds] });
|
||||
@@ -207,7 +207,7 @@ function resolveCurrentSidepanelCapabilities() {
|
||||
visibleGroupIds: ['service-account', 'openai-plus', 'openai-phone'],
|
||||
effectivePanelMode: 'cpa',
|
||||
panelMode: 'cpa',
|
||||
effectiveSourceId: 'cpa',
|
||||
effectiveTargetId: 'cpa',
|
||||
};
|
||||
}
|
||||
const document = {
|
||||
@@ -228,7 +228,7 @@ return {
|
||||
|
||||
assert.deepEqual(
|
||||
api.calls.map((entry) => entry.type),
|
||||
['render-flow', 'render-source', 'groups', 'plus', 'phone']
|
||||
['render-flow', 'render-target', 'groups', 'plus', 'phone']
|
||||
);
|
||||
assert.equal(api.selectFlow.value, 'openai');
|
||||
assert.equal(api.selectPanelMode.value, 'cpa');
|
||||
|
||||
@@ -2,6 +2,14 @@ const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
function loadSourceRegistry() {
|
||||
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
|
||||
const sourceRegistrySource = fs.readFileSync('shared/source-registry.js', 'utf8');
|
||||
const globalScope = {};
|
||||
new Function('self', `${flowRegistrySource}; ${sourceRegistrySource}; return self;`)(globalScope);
|
||||
return globalScope.MultiPageSourceRegistry.createSourceRegistry();
|
||||
}
|
||||
|
||||
test('background imports shared source registry module', () => {
|
||||
const source = fs.readFileSync('background.js', 'utf8');
|
||||
assert.match(source, /shared\/flow-registry\.js/);
|
||||
@@ -22,42 +30,24 @@ test('manifest loads shared source registry before content utils in static bundl
|
||||
}
|
||||
});
|
||||
|
||||
test('manifest ships a static Kiro auth bundle for cross-page recovery', () => {
|
||||
test('manifest no longer ships a static Kiro content bundle', () => {
|
||||
const manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8'));
|
||||
const kiroEntry = (manifest.content_scripts || []).find((entry) => {
|
||||
const matches = Array.isArray(entry.matches) ? entry.matches : [];
|
||||
return matches.includes('https://view.awsapps.com/*')
|
||||
&& matches.includes('https://signin.aws/*')
|
||||
&& matches.includes('https://signin.aws.amazon.com/*')
|
||||
&& matches.includes('https://profile.aws/*')
|
||||
&& matches.includes('https://profile.aws.amazon.com/*');
|
||||
const hasStaticKiroBundle = (manifest.content_scripts || []).some((entry) => {
|
||||
const scripts = Array.isArray(entry.js) ? entry.js : [];
|
||||
return scripts.includes('content/kiro/register-page.js')
|
||||
|| scripts.includes('content/kiro/desktop-authorize-page.js');
|
||||
});
|
||||
|
||||
assert.ok(kiroEntry, 'missing static Kiro auth content script entry');
|
||||
assert.deepEqual(kiroEntry.js, [
|
||||
'shared/source-registry.js',
|
||||
'content/utils.js',
|
||||
'content/kiro-device-auth-page.js',
|
||||
]);
|
||||
assert.equal(hasStaticKiroBundle, false);
|
||||
});
|
||||
|
||||
test('shared source registry exposes canonical source, alias, detection, and ready policies', () => {
|
||||
const flowRegistrySource = fs.readFileSync('shared/flow-registry.js', 'utf8');
|
||||
const source = fs.readFileSync('shared/source-registry.js', 'utf8');
|
||||
const api = new Function('self', `${flowRegistrySource}; ${source}; return self.MultiPageSourceRegistry;`)({});
|
||||
const registry = api.createSourceRegistry();
|
||||
test('shared source registry exposes canonical Kiro sources and drivers', () => {
|
||||
const registry = loadSourceRegistry();
|
||||
|
||||
assert.equal(registry.resolveCanonicalSource('signup-page'), 'openai-auth');
|
||||
assert.deepEqual(registry.getSourceKeys('signup-page'), ['openai-auth', 'signup-page']);
|
||||
assert.equal(registry.getSourceLabel('openai-auth'), '认证页');
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'openai-auth',
|
||||
'https://chatgpt.com/',
|
||||
'https://auth.openai.com/authorize?client_id=test'
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
registry.detectSourceFromLocation({
|
||||
url: 'https://auth.openai.com/create-account',
|
||||
@@ -65,6 +55,21 @@ test('shared source registry exposes canonical source, alias, detection, and rea
|
||||
}),
|
||||
'openai-auth'
|
||||
);
|
||||
assert.equal(
|
||||
registry.detectSourceFromLocation({
|
||||
url: 'https://view.awsapps.com/start',
|
||||
hostname: 'view.awsapps.com',
|
||||
}),
|
||||
'kiro-register-page'
|
||||
);
|
||||
assert.equal(
|
||||
registry.detectSourceFromLocation({
|
||||
injectedSource: 'kiro-desktop-authorize',
|
||||
url: 'https://signin.aws/register',
|
||||
hostname: 'signin.aws',
|
||||
}),
|
||||
'kiro-desktop-authorize'
|
||||
);
|
||||
assert.equal(
|
||||
registry.detectSourceFromLocation({
|
||||
url: 'https://example.com/',
|
||||
@@ -72,29 +77,10 @@ test('shared source registry exposes canonical source, alias, detection, and rea
|
||||
}),
|
||||
'unknown-source'
|
||||
);
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://view.awsapps.com/start',
|
||||
hostname: 'view.awsapps.com',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://signin.aws/register',
|
||||
hostname: 'signin.aws',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://profile.aws/complete',
|
||||
hostname: 'profile.aws',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://signin.aws.amazon.com/register',
|
||||
hostname: 'signin.aws.amazon.com',
|
||||
}), 'kiro-device-auth');
|
||||
assert.equal(registry.detectSourceFromLocation({
|
||||
url: 'https://profile.aws.amazon.com/complete',
|
||||
hostname: 'profile.aws.amazon.com',
|
||||
}), 'kiro-device-auth');
|
||||
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'kiro-register-page',
|
||||
'https://signin.aws/register',
|
||||
'https://view.awsapps.com/start'
|
||||
),
|
||||
@@ -102,7 +88,7 @@ test('shared source registry exposes canonical source, alias, detection, and rea
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'kiro-desktop-authorize',
|
||||
'https://profile.aws/complete',
|
||||
'https://signin.aws/register'
|
||||
),
|
||||
@@ -110,38 +96,24 @@ test('shared source registry exposes canonical source, alias, detection, and rea
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'https://profile.aws.amazon.com/complete',
|
||||
'https://signin.aws.amazon.com/register'
|
||||
),
|
||||
true
|
||||
);
|
||||
assert.equal(
|
||||
registry.matchesSourceUrlFamily(
|
||||
'kiro-device-auth',
|
||||
'kiro-desktop-authorize',
|
||||
'https://oidc.us-east-1.amazonaws.com/authorize',
|
||||
'https://view.awsapps.com/start'
|
||||
),
|
||||
true
|
||||
);
|
||||
|
||||
assert.equal(registry.shouldReportReadyForFrame('mail-163', true), false);
|
||||
assert.equal(registry.shouldReportReadyForFrame('unknown-source', false), false);
|
||||
assert.equal(registry.shouldReportReadyForFrame('kiro-register-page', true), false);
|
||||
assert.equal(registry.shouldReportReadyForFrame('kiro-desktop-authorize', true), false);
|
||||
assert.equal(registry.getCleanupOwnerSource('oauth-localhost-callback'), 'openai-auth');
|
||||
|
||||
assert.equal(registry.driverAcceptsCommand('openai-auth', 'submit-signup-email'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('openai-auth', 'post-login-phone-verification'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('openai-auth', 'bind-email'), true);
|
||||
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);
|
||||
assert.equal(registry.driverAcceptsCommand('content/kiro/register-page', 'kiro-submit-password'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('content/kiro/desktop-authorize-page', 'kiro-complete-desktop-authorize'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('background/kiro-register', 'kiro-open-register-page'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('background/kiro-desktop-authorize', 'kiro-start-desktop-authorize'), true);
|
||||
assert.equal(registry.driverAcceptsCommand('background/kiro-publisher-kiro-rs', 'kiro-upload-credential'), true);
|
||||
});
|
||||
|
||||
@@ -167,35 +167,41 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
assert.deepStrictEqual(
|
||||
kiroSteps.map((step) => step.key),
|
||||
[
|
||||
'kiro-start-device-login',
|
||||
'kiro-open-register-page',
|
||||
'kiro-submit-email',
|
||||
'kiro-submit-name',
|
||||
'kiro-submit-verification-code',
|
||||
'kiro-fill-password',
|
||||
'kiro-confirm-access',
|
||||
'kiro-submit-password',
|
||||
'kiro-complete-register-consent',
|
||||
'kiro-start-desktop-authorize',
|
||||
'kiro-complete-desktop-authorize',
|
||||
'kiro-upload-credential',
|
||||
]
|
||||
);
|
||||
assert.equal(kiroSteps.every((step) => step.flowId === 'kiro'), true);
|
||||
assert.equal(kiroSteps[0].driverId, 'background/kiro-device-auth');
|
||||
assert.equal(kiroSteps[6].sourceId, 'kiro-rs-admin');
|
||||
assert.equal(kiroSteps[0].title, '启动设备登录');
|
||||
assert.equal(kiroSteps[0].driverId, 'background/kiro-register');
|
||||
assert.equal(kiroSteps[8].sourceId, 'kiro-rs-admin');
|
||||
assert.equal(kiroSteps[0].title, '打开注册页');
|
||||
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.equal(kiroSteps[5].title, '完成注册授权');
|
||||
assert.equal(kiroSteps[6].title, '启动桌面授权');
|
||||
assert.equal(kiroSteps[7].title, '完成桌面授权');
|
||||
assert.equal(kiroSteps[8].title, '上传凭据到 kiro.rs');
|
||||
assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'kiro' }), [1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
||||
assert.equal(api.getLastStepId({ activeFlowId: 'kiro' }), 9);
|
||||
assert.deepStrictEqual(
|
||||
api.getNodes({ activeFlowId: 'kiro' }).map((node) => node.next),
|
||||
[
|
||||
['kiro-submit-email'],
|
||||
['kiro-submit-name'],
|
||||
['kiro-submit-verification-code'],
|
||||
['kiro-fill-password'],
|
||||
['kiro-confirm-access'],
|
||||
['kiro-submit-password'],
|
||||
['kiro-complete-register-consent'],
|
||||
['kiro-start-desktop-authorize'],
|
||||
['kiro-complete-desktop-authorize'],
|
||||
['kiro-upload-credential'],
|
||||
[],
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user