mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-08-03 19:12:04 +08:00
fix(pi): refresh desired takeover state after failures
This commit is contained in:
@@ -122,7 +122,7 @@
|
||||
## Scope audit(主工程预审检查点)
|
||||
|
||||
以主工程基线 `26a95aeb059235612c665d6b6bcd92fde9c3572e` 计,终态候选为
|
||||
140 个文件、约 +15.8k 净行,触发历史 90 文件/12k 净行审计线。审计结论为
|
||||
141 个文件、约 +16.9k 净行,触发历史 90 文件/12k 净行审计线。审计结论为
|
||||
**范围大但未越界**:
|
||||
|
||||
- Rust 70 文件:Pi catalog/typed endpoints、共享文件与 ownership、gateway/runtime/
|
||||
@@ -132,7 +132,7 @@
|
||||
- 前端与构建配置 55 文件:3 个 Pi 专用 panel/form、typed IPC/query、既有导航/
|
||||
设置/代理/使用统计接线、四语 i18n 与 Windows 进程树能力;没有复制第二套 app
|
||||
shell。
|
||||
- 前端测试 12 文件:专用表单/prompt/usage 测试及既有跨 app matrix 增量。
|
||||
- 前端测试 13 文件:专用表单/prompt/usage 测试及既有跨 app matrix 增量。
|
||||
- 证据与文档 3 文件:transport capture、本文账本、extensions 设计附录。
|
||||
- `schema.rs`、`migration.rs`、`backup.rs`、三份认证套件及七个 pinned fixtures
|
||||
均零改动;restore、themes 与 extensions 实现零文件。
|
||||
|
||||
@@ -45,21 +45,21 @@ export function ProxyToggle({ className, activeApp }: ProxyToggleProps) {
|
||||
defaultValue: `${appLabel} routing is desired but currently degraded; CC Switch will retry it`,
|
||||
})
|
||||
: takeoverEnabled
|
||||
? isRunning
|
||||
? t("proxy.takeover.tooltip.active", {
|
||||
? isRunning
|
||||
? t("proxy.takeover.tooltip.active", {
|
||||
appLabel,
|
||||
address: status?.address,
|
||||
port: status?.port,
|
||||
defaultValue: `${appLabel} 已接管 - ${status?.address}:${status?.port}\n切换该应用供应商为热切换`,
|
||||
})
|
||||
: t("proxy.takeover.tooltip.broken", {
|
||||
appLabel,
|
||||
defaultValue: `${appLabel} 已接管,但代理服务未运行`,
|
||||
})
|
||||
: t("proxy.takeover.tooltip.inactive", {
|
||||
appLabel,
|
||||
address: status?.address,
|
||||
port: status?.port,
|
||||
defaultValue: `${appLabel} 已接管 - ${status?.address}:${status?.port}\n切换该应用供应商为热切换`,
|
||||
})
|
||||
: t("proxy.takeover.tooltip.broken", {
|
||||
appLabel,
|
||||
defaultValue: `${appLabel} 已接管,但代理服务未运行`,
|
||||
})
|
||||
: t("proxy.takeover.tooltip.inactive", {
|
||||
appLabel,
|
||||
defaultValue: `接管 ${appLabel} 的 Live 配置,让该应用请求走本地代理`,
|
||||
});
|
||||
defaultValue: `接管 ${appLabel} 的 Live 配置,让该应用请求走本地代理`,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -129,9 +129,6 @@ export function useProxyStatus() {
|
||||
}),
|
||||
{ closeButton: true },
|
||||
);
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: proxyKeys.status });
|
||||
queryClient.invalidateQueries({ queryKey: proxyKeys.takeoverStatus });
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
const detail =
|
||||
@@ -144,6 +141,20 @@ export function useProxyStatus() {
|
||||
}),
|
||||
);
|
||||
},
|
||||
// Pi persists desired takeover before attempting listener/runtime work.
|
||||
// A rejected IPC can therefore still have changed authoritative state.
|
||||
onSettled: async () => {
|
||||
await Promise.all([
|
||||
queryClient.refetchQueries({
|
||||
queryKey: proxyKeys.status,
|
||||
type: "active",
|
||||
}),
|
||||
queryClient.refetchQueries({
|
||||
queryKey: proxyKeys.takeoverStatus,
|
||||
type: "active",
|
||||
}),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -131,4 +131,74 @@ describe("useProxyStatus", () => {
|
||||
expect(invokeMock).toHaveBeenCalledWith("start_proxy_server");
|
||||
expect(invokeMock).toHaveBeenCalledWith("stop_proxy_server");
|
||||
});
|
||||
|
||||
it("refreshes desired takeover state even when activation rejects", async () => {
|
||||
let desiredPi = false;
|
||||
invokeMock.mockImplementation((command: string) => {
|
||||
if (command === "get_proxy_status") {
|
||||
return Promise.resolve({
|
||||
running: false,
|
||||
address: "127.0.0.1",
|
||||
port: 15721,
|
||||
active_connections: 0,
|
||||
total_requests: 0,
|
||||
success_requests: 0,
|
||||
failed_requests: 0,
|
||||
success_rate: 0,
|
||||
uptime_seconds: 0,
|
||||
current_provider: null,
|
||||
current_provider_id: null,
|
||||
last_request_at: null,
|
||||
last_error: null,
|
||||
failover_count: 0,
|
||||
});
|
||||
}
|
||||
if (command === "get_proxy_takeover_status") {
|
||||
return Promise.resolve({
|
||||
claude: false,
|
||||
codex: false,
|
||||
gemini: false,
|
||||
grokbuild: false,
|
||||
opencode: false,
|
||||
openclaw: false,
|
||||
pi: desiredPi,
|
||||
piOperationalState: desiredPi ? "degraded" : "disabled",
|
||||
});
|
||||
}
|
||||
if (command === "set_proxy_takeover_for_app") {
|
||||
desiredPi = true;
|
||||
return Promise.reject(new Error("listener unavailable"));
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
});
|
||||
|
||||
const { wrapper, queryClient } = createWrapper();
|
||||
const { result, rerender } = renderHook(() => useProxyStatus(), {
|
||||
wrapper,
|
||||
});
|
||||
await waitFor(() => expect(result.current.takeoverStatus).toBeDefined());
|
||||
|
||||
await expect(
|
||||
act(async () => {
|
||||
await result.current.setTakeoverForApp({
|
||||
appType: "pi",
|
||||
enabled: true,
|
||||
});
|
||||
}),
|
||||
).rejects.toThrow("listener unavailable");
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
invokeMock.mock.calls.filter(
|
||||
([command]) => command === "get_proxy_takeover_status",
|
||||
).length,
|
||||
).toBeGreaterThan(1),
|
||||
);
|
||||
expect(queryClient.getQueryData(proxyKeys.takeoverStatus)).toMatchObject({
|
||||
pi: true,
|
||||
piOperationalState: "degraded",
|
||||
});
|
||||
rerender();
|
||||
await waitFor(() => expect(result.current.takeoverStatus?.pi).toBe(true));
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user