Add codex2api as a protocol-first OAuth source

This keeps the existing CPA, SUB2API, and contribution flows intact
while introducing codex2api as a separate source that plugs into
Step 7 and Step 10 through backend APIs instead of page DOM.

The sidepanel now exposes codex2api settings, preserves the built-in
local default through placeholder-only UI, and accepts user-provided
public admin URLs. Step 7 now treats missing or invalid management
secrets as terminal config errors so the flow stops instead of retrying
needlessly.

Constraint: New source must be additive and must not break existing CPA/SUB2API/contribution paths
Rejected: Drive codex2api through admin page button clicks | too brittle against frontend DOM changes
Rejected: Reuse contribution mode as a source surface | violates existing panelMode/runtime-mode boundary
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep codex2api on the protocol path unless its API becomes insufficient; do not add a panel content script without proving the protocol path cannot work
Tested: npm test; targeted codex2api/auth retry loops repeated 40 times; full suite repeated 5 times
Not-tested: Real browser run against a live codex2api instance with a real Admin Secret and OpenAI authorization
This commit is contained in:
initiatione
2026-04-22 13:57:21 +08:00
parent 27c8faa2a0
commit f8b919e883
21 changed files with 720 additions and 32 deletions
+2
View File
@@ -24,6 +24,8 @@
dom.rowSub2ApiPassword,
dom.rowSub2ApiGroup,
dom.rowSub2ApiDefaultProxy,
dom.rowCodex2ApiUrl,
dom.rowCodex2ApiAdminKey,
dom.rowCustomPassword,
dom.rowAccountRunHistoryHelperBaseUrl,
].filter(Boolean);
+11
View File
@@ -806,6 +806,17 @@ header {
flex-shrink: 0;
}
#row-codex2api-url .data-label,
#row-codex2api-admin-key .data-label {
width: 76px;
}
#row-codex2api-url .data-label {
font-size: 10px;
letter-spacing: 0.02em;
text-transform: none;
}
.data-value {
font-size: 13px;
color: var(--text-muted);
+11
View File
@@ -112,6 +112,7 @@
<select id="select-panel-mode" class="data-select">
<option value="cpa">CPA 面板</option>
<option value="sub2api">SUB2API</option>
<option value="codex2api">Codex2API</option>
</select>
</div>
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
@@ -192,6 +193,16 @@
<input type="text" id="input-sub2api-default-proxy" class="data-input"
placeholder="留空则不使用代理;或填写代理名称 / ID" />
</div>
<div class="data-row" id="row-codex2api-url" style="display:none;">
<span class="data-label">Codex2API</span>
<input type="text" id="input-codex2api-url" class="data-input"
placeholder="http://localhost:8080/admin/accounts" />
</div>
<div class="data-row" id="row-codex2api-admin-key" style="display:none;">
<span class="data-label">管理密钥</span>
<input type="password" id="input-codex2api-admin-key" class="data-input"
placeholder="请输入 Codex2API Admin Secret" />
</div>
<div class="data-row" id="row-custom-password">
<span class="data-label">账户密码</span>
<div class="input-with-icon">
+34 -4
View File
@@ -94,6 +94,10 @@ const rowSub2ApiGroup = document.getElementById('row-sub2api-group');
const inputSub2ApiGroup = document.getElementById('input-sub2api-group');
const rowSub2ApiDefaultProxy = document.getElementById('row-sub2api-default-proxy');
const inputSub2ApiDefaultProxy = document.getElementById('input-sub2api-default-proxy');
const rowCodex2ApiUrl = document.getElementById('row-codex2api-url');
const inputCodex2ApiUrl = document.getElementById('input-codex2api-url');
const rowCodex2ApiAdminKey = document.getElementById('row-codex2api-admin-key');
const inputCodex2ApiAdminKey = document.getElementById('input-codex2api-admin-key');
const rowCustomPassword = document.getElementById('row-custom-password');
const selectMailProvider = document.getElementById('select-mail-provider');
const btnMailLogin = document.getElementById('btn-mail-login');
@@ -1479,6 +1483,8 @@ function collectSettingsPayload() {
sub2apiPassword: inputSub2ApiPassword.value,
sub2apiGroupName: inputSub2ApiGroup.value.trim(),
sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(),
codex2apiUrl: inputCodex2ApiUrl.value.trim(),
codex2apiAdminKey: inputCodex2ApiAdminKey.value.trim(),
...(contributionModeEnabled ? {} : {
customPassword: inputPassword.value,
}),
@@ -1858,6 +1864,8 @@ function applySettingsState(state) {
inputSub2ApiPassword.value = state?.sub2apiPassword || '';
inputSub2ApiGroup.value = state?.sub2apiGroupName || '';
inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || '';
inputCodex2ApiUrl.value = state?.codex2apiUrl || '';
inputCodex2ApiAdminKey.value = state?.codex2apiAdminKey || '';
const restoredMailProvider = isCustomMailProvider(state?.mailProvider)
|| [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', '163', '163-vip', 'qq', 'inbucket', '2925', 'cloudflare-temp-email'].includes(String(state?.mailProvider || '').trim())
? String(state?.mailProvider || '163').trim()
@@ -2864,18 +2872,24 @@ async function saveCloudflareTempEmailDomainSettings(domains, activeDomain, opti
function updatePanelModeUI() {
const useSub2Api = selectPanelMode.value === 'sub2api';
rowVpsUrl.style.display = useSub2Api ? 'none' : '';
rowVpsPassword.style.display = useSub2Api ? 'none' : '';
rowLocalCpaStep9Mode.style.display = useSub2Api ? 'none' : '';
const useCodex2Api = selectPanelMode.value === 'codex2api';
const useCpa = !useSub2Api && !useCodex2Api;
rowVpsUrl.style.display = useCpa ? '' : 'none';
rowVpsPassword.style.display = useCpa ? '' : 'none';
rowLocalCpaStep9Mode.style.display = useCpa ? '' : 'none';
rowSub2ApiUrl.style.display = useSub2Api ? '' : 'none';
rowSub2ApiEmail.style.display = useSub2Api ? '' : 'none';
rowSub2ApiPassword.style.display = useSub2Api ? '' : 'none';
rowSub2ApiGroup.style.display = useSub2Api ? '' : 'none';
rowSub2ApiDefaultProxy.style.display = useSub2Api ? '' : 'none';
rowCodex2ApiUrl.style.display = useCodex2Api ? '' : 'none';
rowCodex2ApiAdminKey.style.display = useCodex2Api ? '' : 'none';
const step9Btn = document.querySelector('.step-btn[data-step-key="platform-verify"]');
if (step9Btn) {
step9Btn.textContent = useSub2Api ? 'SUB2API 回调验证' : 'CPA 回调验证';
step9Btn.textContent = useSub2Api
? 'SUB2API 回调验证'
: (useCodex2Api ? 'Codex2API 回调验证' : 'CPA 回调验证');
}
}
@@ -4270,6 +4284,22 @@ inputSub2ApiDefaultProxy.addEventListener('blur', () => {
saveSettings({ silent: true }).catch(() => { });
});
inputCodex2ApiUrl.addEventListener('input', () => {
markSettingsDirty(true);
scheduleSettingsAutoSave();
});
inputCodex2ApiUrl.addEventListener('blur', () => {
saveSettings({ silent: true }).catch(() => { });
});
inputCodex2ApiAdminKey.addEventListener('input', () => {
markSettingsDirty(true);
scheduleSettingsAutoSave();
});
inputCodex2ApiAdminKey.addEventListener('blur', () => {
saveSettings({ silent: true }).catch(() => { });
});
inputEmailPrefix.addEventListener('input', () => {
maybeClearGeneratedAliasAfterEmailPrefixChange().catch(() => { });
syncManagedAliasBaseEmailDraftFromInput();