Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 41 additions & 6 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,29 @@ jobs:
uses: dtolnay/rust-toolchain@stable

- name: Install NSIS
run: choco install nsis -y
shell: pwsh
run: |
$makensis = $null
for ($attempt = 1; $attempt -le 3; $attempt++) {
choco install nsis -y --no-progress
# Chocolatey 可能在下载失败后返回成功,必须实际验证编译器。
$candidates = @(
"${env:ProgramFiles(x86)}\NSIS\makensis.exe",
"$env:ProgramFiles\NSIS\makensis.exe"
)
$makensis = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $makensis) {
$command = Get-Command makensis.exe -ErrorAction SilentlyContinue
if ($command) { $makensis = $command.Source }
}
if ($makensis) {
& $makensis /VERSION
if ($LASTEXITCODE -eq 0) { break }
}
$makensis = $null
if ($attempt -lt 3) { Start-Sleep -Seconds (10 * $attempt) }
}
if (-not $makensis) { throw "NSIS 安装失败或编译器不可用,停止 PR 构建。" }

- name: Install frontend dependencies
working-directory: apps/codex-plus-manager
Expand Down Expand Up @@ -68,13 +90,26 @@ jobs:
if (-not $version -or $version -eq "main") {
$version = "0.0.0-${{ github.run_number }}"
}
$makensis = "${env:ProgramFiles(x86)}\NSIS\makensis.exe"
if (-not (Test-Path $makensis)) {
$makensis = "makensis"
# NSIS 的 64 位安装目录与 Chocolatey PATH 更新均需要兼容。
$candidates = @(
"${env:ProgramFiles(x86)}\NSIS\makensis.exe",
"$env:ProgramFiles\NSIS\makensis.exe"
)
$makensis = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $makensis) {
$command = Get-Command makensis.exe -ErrorAction SilentlyContinue
if ($command) { $makensis = $command.Source }
}
if (-not $makensis) {
throw "NSIS 编译器未找到,检查 Chocolatey 安装结果。"
}
Push-Location scripts/installer/windows
& $makensis "/INPUTCHARSET" "UTF8" "/DVERSION=$version" CodexPlusPlus.nsi
Pop-Location
try {
& $makensis "/INPUTCHARSET" "UTF8" "/DVERSION=$version" CodexPlusPlus.nsi
if ($LASTEXITCODE -ne 0) { throw "NSIS 打包失败,退出码 $LASTEXITCODE" }
} finally {
Pop-Location
}

- name: Upload Windows binaries
uses: actions/upload-artifact@v4
Expand Down
14 changes: 11 additions & 3 deletions apps/codex-plus-launcher/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ async fn run_embedded_session(
|| repair_session_index_automatically(hooks.index_repair_checks_setting()),
)
.await;
handle.stop_isolated_api_quota_gate().await;
stopped_by_host = host_stop;
result
}
Expand Down Expand Up @@ -1818,9 +1819,16 @@ mod tests {
body.matches("repair_session_index_automatically(").count(),
1
);
assert!(body.contains(
"wait_for_session_exit_or_stop(handle.wait_for_codex_exit(), &mut stop)"
));
assert!(
body.contains("wait_for_session_exit_or_stop(handle.wait_for_codex_exit(), &mut stop)")
);
let quota_stop = body
.find("handle.stop_isolated_api_quota_gate().await")
.unwrap();
let helper_stop = body
.find("hooks.shutdown_helper(options.helper_port).await")
.unwrap();
assert!(quota_stop < helper_stop);
let launch_start = source.find("async fn launcher_session(").unwrap();
let launch_end = source[launch_start..]
.find("async fn activate_and_write_running_status(")
Expand Down
40 changes: 28 additions & 12 deletions apps/codex-plus-manager/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ type BackendSettings = WhaleBalanceSettings & {
providerSyncLastSelectedProvider: string;
relayProfilesEnabled: boolean;
enhancementsEnabled: boolean;
codexAppStandaloneQuotaUnlock: boolean;
codexAppPluginMarketplaceUnlock: boolean;
codexAppModelWhitelistUnlock: boolean;
codexAppSessionDelete: boolean;
Expand Down Expand Up @@ -1058,6 +1059,7 @@ const defaultSettings: BackendSettings = {
providerSyncLastSelectedProvider: "",
relayProfilesEnabled: true,
enhancementsEnabled: true,
codexAppStandaloneQuotaUnlock: false,
codexAppPluginMarketplaceUnlock: true,
codexAppModelWhitelistUnlock: true,
codexAppSessionDelete: true,
Expand Down Expand Up @@ -5705,18 +5707,32 @@ function EnhanceScreen({
<div className="settings-page enhance-page">
<div className="enhance-page-controls" id="enhance-page-controls">
<div className="enhance-page-toolbar">
<label className="switch-row compact enhance-master-switch">
<input
checked={form.enhancementsEnabled}
onChange={(event) => onFormChange({ ...form, enhancementsEnabled: event.currentTarget.checked })}
type="checkbox"
/>
<span>
<strong>{t("启用 Codex增强")}</strong>
<small>{t("关闭后暂停增强功能,服务配置会保留。")}</small>
</span>
<ToggleVisual />
</label>
<div className="enhance-primary-switches">
<label className="switch-row compact enhance-master-switch">
<input
checked={form.enhancementsEnabled}
onChange={(event) => onFormChange({ ...form, enhancementsEnabled: event.currentTarget.checked })}
type="checkbox"
/>
<span>
<strong>{t("启用 Codex增强")}</strong>
<small>{t("关闭后暂停增强功能,服务配置会保留。")}</small>
</span>
<ToggleVisual />
</label>
<label className="switch-row compact enhance-master-switch">
<input
checked={form.codexAppStandaloneQuotaUnlock}
onChange={(event) => setEnhanceFlag("codexAppStandaloneQuotaUnlock", event.currentTarget.checked)}
type="checkbox"
/>
<span>
<strong>{t("独立额度解锁")}</strong>
<small>{t("官方登录 + 混入 API Key")}</small>
</span>
<ToggleVisual />
</label>
</div>
<div className="enhance-save-actions">
<span className={dirty ? "is-dirty" : ""} role="status">{dirty ? t("有未保存修改") : t("已保存")}</span>
<Button disabled={!dirty} onClick={() => void actions.saveSettings()}>
Expand Down
14 changes: 14 additions & 0 deletions apps/codex-plus-manager/src/enhancement-settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,17 @@ test("enhancement saving is an explicit permanent top action", () => {
const firstPane = elements(enhance).find((element) => attribute(element, "role") === "tabpanel");
assert.ok(firstPane && saves[0].pos < firstPane.pos);
});

test("standalone quota unlock remains editable below the master switch when enhancements are off", () => {
const enhance = component("EnhanceScreen");
const master = fieldControl(enhance, "form.enhancementsEnabled");
const standalone = fieldControl(enhance, "form.codexAppStandaloneQuotaUnlock");
assert.equal(attribute(standalone, "disabled"), undefined);
assert.equal(isConditional(standalone, enhance), false);
assert.ok(master.pos < standalone.pos);
const firstPane = elements(enhance).find((element) => attribute(element, "role") === "tabpanel");
assert.ok(firstPane && standalone.pos < firstPane.pos);
const handler = eventHandlerNodes(enhance, standalone, "onChange");
assert.ok(handler.some((node) => ts.isCallExpression(node) && node.expression.getText(app) === "onFormChange"));
assert.ok(handler.some((node) => ts.isStringLiteral(node) && node.text === "codexAppStandaloneQuotaUnlock"));
});
2 changes: 2 additions & 0 deletions apps/codex-plus-manager/src/i18n-en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@ export const EN_PLAIN: Record<string, string> = {
"启用": "Enable",
"启用 Codex 图片覆盖层": "Enable Codex image overlay",
"启用 Codex增强": "Enable Codex enhancements",
"独立额度解锁": "Standalone quota unlock",
"官方登录 + 混入 API Key": "Official login + mixed API key",
"启用供应商配置切换": "Enable provider configuration switching",
"启用此扩展项": "Enable this entry",
"包含测试模型、上下文大小与压缩阈值;留空即沿用全局默认值。":
Expand Down
8 changes: 8 additions & 0 deletions apps/codex-plus-manager/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -8643,6 +8643,14 @@ select {
padding: 14px 18px;
}

.enhance-primary-switches {
display: grid;
gap: 14px;
flex: 1;
min-width: 0;
max-width: 480px;
}

.enhance-master-switch.switch-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
Expand Down
Loading
Loading