fix(ai): 仅为 paste-code provider 合成默认手动粘贴码提示

机器人指出之前的 CLI 侧 gating 是无效的:runLocalLogin 对非 paste-code provider
省略 onManualCodeInput,但 AuthStorage.login 仍以 ctrl.onManualCodeInput ??
manualCodeInput 注入默认值,因此 loopback OAuth provider 的 OAuthCallbackFlow
仍会让 readline 粘贴提示与 HTTP 回调竞争;回调先到时该提示悬挂,终端进入
脏/阻塞状态。

在唯一汇聚点 AuthStorage.login 做权威 gating:

- 仅当 provider 属于 PASTE_CODE_LOGIN_PROVIDERS 时才合成默认 manualCodeInput;
  loopback provider 不再获得手动码竞争。
- 调用方显式传入的 onManualCodeInput 对任意 provider 仍被透传(逃生舱)。
- 该修复覆盖所有调用方,不止 auth-broker CLI。
- CLI 侧的 usesManualInput gating 保留为纵深防御,并更新注释指明 storage 层
  才是权威闸门,纠正机器人指出的“只在此处省略”误导性表述。

新增针对 storage 契约的回归测试(auth-storage-manual-code-gate.test.ts):
loopback provider 不被注入默认提示;显式提示对 loopback 仍透传;paste-code
provider(gitlab-duo-agent)在调用方省略时被合成默认提示并经 onPrompt 路由。
This commit is contained in:
jiwangyihao
2026-06-26 16:13:25 +08:00
parent af32c22ffe
commit ec00294462
4 changed files with 126 additions and 6 deletions
@@ -213,10 +213,13 @@ async function runLocalLogin(provider: OAuthProvider): Promise<void> {
await storage.reload();
try {
// Only paste-code providers (fixed non-loopback redirect, e.g. GitLab Duo
// Agent's vscode:// URI) get the manual paste fallback. For normal loopback
// providers `onManualCodeInput` would make OAuthCallbackFlow race a readline
// prompt against the HTTP callback; if the callback wins, the outstanding
// prompt is never cancelled and leaves the terminal in a dirty/blocked state.
// Agent's vscode:// URI) get the manual paste fallback. An explicit
// `onManualCodeInput` is honored for ANY provider (the storage escape hatch),
// so for loopback providers we must not pass it: it would make
// `OAuthCallbackFlow` race a readline prompt against the HTTP callback and, if
// the callback wins, leave that prompt outstanding (dirty/blocked terminal).
// `AuthStorage.login` independently refuses to synthesize the default prompt
// for non-paste-code providers, so this is defense-in-depth on the same gate.
const usesManualInput = PASTE_CODE_LOGIN_PROVIDERS.has(provider);
await storage.login(provider, {
onAuth({ url, instructions }) {