前端:删掉由 invoke 拒绝驱动的认证重试

- `directCodexSession.ts` 改名 `directCodexSessionKeepalive.ts`,只保留会话保活常量,删除 `withDirectCodexSessionRefresh` 的"刷新 + 重跑整轮"及其登录失效识别
- 发送回合与终止回合两处调用点直接 invoke,不再包一层重试
- controller 的 catch TODO 更新为新形状:命令只接单、拒单返回 typed 错误、整轮结果只由事件载荷回答
This commit is contained in:
2026-09-23 17:50:58 +08:00
parent e0d88fe8e4
commit e776aa1084
3 changed files with 22 additions and 67 deletions
@@ -39,10 +39,7 @@ import {
isDirectCodexTurnAlreadyRunningError,
isDirectCodexTurnInterruptedError,
} from '../conversation/directCodexConversation';
import {
DIRECT_CODEX_SESSION_KEEPALIVE_MS,
withDirectCodexSessionRefresh,
} from '../conversation/directCodexSession';
import { DIRECT_CODEX_SESSION_KEEPALIVE_MS } from '../conversation/directCodexSessionKeepalive';
import {
type DirectCodexTurnAttachment,
toDirectCodexTurnAttachments,
@@ -569,23 +566,22 @@ export function useDirectProjectChatController({
currentPlatformSessionGeneration,
);
try {
await withDirectCodexSessionRefresh(() =>
invoke<string>('chat_with_game_creator_direct_codex', {
projectPath: nextProjectPath,
clientTurnId: input.clientTurnId,
userItem: input.userItem,
analyticsAttemptId: runAnalytics.nextAttempt(),
...(input.creationType ? { creationType: input.creationType } : {}),
}),
);
await invoke<string>('chat_with_game_creator_direct_codex', {
projectPath: nextProjectPath,
clientTurnId: input.clientTurnId,
userItem: input.userItem,
analyticsAttemptId: runAnalytics.nextAttempt(),
...(input.creationType ? { creationType: input.creationType } : {}),
});
} finally {
runAnalytics.settle();
}
// 清单刷新统一交给 startTurn 的 finally:成功与报错路径都覆盖,且只读一次。
// TODO(Direct 命令接单化,未实施):下面这个 catch 现在兼职"接单被拒"与"回合失败"两种回执。
// 计划(`docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`,草案)是让命令只负责接单
// 整轮结果只由订阅事件的载荷回答:届时这里只剩接单被拒,文案改成这条消息下面的本地助手
// 气泡,状态行改由 reducer 供数据,而 invoke 拒绝驱动的认证重试作废
// 计划(`docs/adr/【ADR】DirectProject命令接单化-2026-09-23.md`)是让命令只负责接单:整轮结果
// 只由订阅事件的载荷回答,拒单则返回 typed 错误——届时这里只剩"拒单",认得的前置 / 参数类按变体
// 给与用户消息同级的提示且不上报,认不得的错误原样抛出交给既有捕获链路,状态行改由 reducer 供数
// invoke 拒绝驱动的认证重试已按该 ADR 删除(`directCodexSessionKeepalive.ts` 只保留会话保活)。
} catch (error) {
if (
isDirectCodexTurnAlreadyRunningError(error) ||
@@ -663,10 +659,11 @@ export function useDirectProjectChatController({
setTurnCancelling(true);
setComposerNotice('正在终止当前回合');
try {
const result = await withDirectCodexSessionRefresh(() =>
invoke<DirectTurnCancelView>('cancel_direct_codex_turn', {
const result = await invoke<DirectTurnCancelView>(
'cancel_direct_codex_turn',
{
projectPath,
}),
},
);
const message = result?.message?.trim();
if (result?.outcome === 'released') {
@@ -1,48 +0,0 @@
import {
currentPlatformSessionGeneration,
requestPlatformSessionRefresh,
} from '../../../../services/platformSession';
/**
* 平台 access token 很短命。DirectProject 一个回合可能横跨图片生成、构建和浏览器验证,
* 所以回合运行期间由客户端保持原生会话新鲜;`platformSession.ts` 的 singleflight
* 会把这里的刷新与 401 触发的刷新合并成同一次请求。
*/
export const DIRECT_CODEX_SESSION_KEEPALIVE_MS = 5 * 60 * 1000;
function isDirectCodexAuthenticationRequired(error: unknown) {
const message = error instanceof Error ? error.message : String(error);
return (
message.includes('authentication-required') ||
message.includes('codex-app-server-error:unauthorized') ||
/kind=codex-app-server-unauthorized(?=\s|$)/.test(message) ||
message.includes('登录已失效')
);
}
/**
* 跑一轮 DirectProject 请求:只有 401/登录失效才刷新会话重试一次,其它错误原样抛出。
*
* 刷新期间账号代际变化(换号、登出)时必须放弃重试:带着旧身份的请求重放会把上一账号
* 的回合打进新账号的对话历史。
*/
export async function withDirectCodexSessionRefresh<T>(
operation: () => Promise<T>,
) {
const generation = currentPlatformSessionGeneration();
try {
return await operation();
} catch (error) {
if (!isDirectCodexAuthenticationRequired(error)) throw error;
if (currentPlatformSessionGeneration() !== generation) throw error;
const refresh = await requestPlatformSessionRefresh();
if (refresh.status === 'failed') throw error;
if (
refresh.status !== 'refreshed' ||
currentPlatformSessionGeneration() !== refresh.generation
) {
throw new Error('登录账号已变化,原对话请求已停止');
}
return operation();
}
}
@@ -0,0 +1,6 @@
/**
* 平台 access token 很短命。DirectProject 一个回合可能横跨图片生成、构建和浏览器验证,
* 所以回合运行期间由客户端保持原生会话新鲜;`platformSession.ts` 的 singleflight
* 会把这里的刷新与 401 触发的刷新合并成同一次请求。
*/
export const DIRECT_CODEX_SESSION_KEEPALIVE_MS = 5 * 60 * 1000;