fix(ai): clarified oauth completion close copy

Updated the OAuth success page so Firefox users are told they can close the tab manually while the existing close button and timeout remain best-effort.

Added callback-server coverage for the served success page copy.

Fixes #4855
This commit is contained in:
roboomp
2026-07-09 17:24:37 +00:00
parent 3f0c2c63a0
commit 5e781a9c7a
4 changed files with 25 additions and 6 deletions
+1
View File
@@ -17,6 +17,7 @@
- Fixed OpenAI Codex WebSocket continuations to treat proxy stale-anchor codes such as `codex_previous_response_stale` as an expired `previous_response_id` chain — same recovery class as the OpenAI-standard `previous_response_not_found` — so the turn is retried with full context instead of surfacing the error to the user ([#4624](https://github.com/can1357/oh-my-pi/issues/4624)).
- Fixed Azure Foundry Anthropic utility requests to omit the structured-output beta whenever strict tools are disabled, preventing `structured_outputs not supported in your workspace` failures for Sonnet 5 compaction ([#4679](https://github.com/can1357/oh-my-pi/issues/4679)).
- Fixed OAuth `launchUrl` advertisement for flows whose redirect never returns to the local callback server: custom-scheme redirects (e.g. GitLab Duo's `vscode://` URI, which `new URL` parses without complaint) and fixed non-loopback hosts no longer receive a `http://localhost:<port>/launch` copy target that misrepresents the callback endpoint and resolves nowhere for remote users.
- Fixed the OAuth completion page copy to tell users they can close the tab manually when browsers such as Firefox ignore best-effort `window.close()` calls. ([#4855](https://github.com/can1357/oh-my-pi/issues/4855))
### Fixed
- Codex load balancing: clear stale persisted and in-memory usage-limit blocks for an `openai-codex` account when a fresh live usage report shows it is allowed and below all limits, including broker-backed gateway snapshots, so traffic returns to recovered accounts instead of funneling to one sibling.
+1 -1
View File
@@ -305,7 +305,7 @@
if (serverState.ok) {
app.classList.add("success", "countdown");
title.textContent = "Authentication Successful";
message.innerHTML = "You have successfully logged in.<br>This window will close automatically.";
message.innerHTML = "You have successfully logged in.<br>You can now close this tab.";
setTimeout(() => window.close(), 3000);
} else {
app.classList.add("error");
@@ -114,6 +114,24 @@ describe("OAuthCallbackFlow /launch route", () => {
await login;
});
it("serves success copy that permits manual tab close", async () => {
const { info, login } = await startFlowAndWaitForAuth();
const authUrl = new URL(info.url);
const redirectUri = authUrl.searchParams.get("redirect_uri");
expect(redirectUri).toMatch(/^http:\/\/localhost:\d+\/callback$/);
const state = authUrl.searchParams.get("state") ?? "";
const callbackResponse = await fetch(`${redirectUri}?code=test-code&state=${encodeURIComponent(state)}`);
expect(callbackResponse.status).toBe(200);
const html = await callbackResponse.text();
expect(html).toContain("Authentication Successful");
expect(html).toContain("You have successfully logged in.<br>You can now close this tab.");
expect(html).toContain("Close Window");
expect(html).not.toContain("This window will close automatically.");
await login;
});
it("suppresses launchUrl and routes /launch to the callback handler when callbackPath is /launch", async () => {
const abort = new AbortController();
const authFired = Promise.withResolvers<OAuthAuthInfo>();
@@ -51,20 +51,20 @@ Decompose first, then {{#if taskBatch}}batch the independent leaves{{else}}issue
{{#if taskBatch}}
task(
context: "# Goal\nReview the auth diff...\n# Constraints\nRead-only...\n# Contract\nReturn findings as severity/file/line/fix...",
context: "# Goal\nReview the auth diff…\n# Constraints\nRead-only…\n# Contract\nReturn findings as severity/file/line/fix…",
tasks: [
{ id: "AuthOwner", role: "Auth Storage Reviewer", assignment: "# Target\npackages/ai/src/auth-storage.ts\n# Change\nTrace credential selection...\n# Acceptance\nReturn confirmed findings only..." },
{ id: "PromptOwner", role: "Prompt Contract Reviewer", assignment: "# Target\npackages/coding-agent/src/prompts/**\n# Change\nCheck active-tool guidance...\n# Acceptance\nReturn mismatches and exact prompt lines..." },
{ id: "AuthOwner", role: "Auth Storage Reviewer", assignment: "# Target\npackages/ai/src/auth-storage.ts\n# Change\nTrace credential selection…\n# Acceptance\nReturn confirmed findings only…" },
{ id: "PromptOwner", role: "Prompt Contract Reviewer", assignment: "# Target\npackages/coding-agent/src/prompts/**\n# Change\nCheck active-tool guidance…\n# Acceptance\nReturn mismatches and exact prompt lines…" },
]
)
{{else}}
task(
role: "Auth Storage Reviewer",
assignment: "# Target\npackages/ai/src/auth-storage.ts\n# Change\nReview the auth diff. Shared contract: read-only; return findings as severity/file/line/fix.\n# Acceptance\nReturn confirmed findings only..."
assignment: "# Target\npackages/ai/src/auth-storage.ts\n# Change\nReview the auth diff. Shared contract: read-only; return findings as severity/file/line/fix.\n# Acceptance\nReturn confirmed findings only…"
)
task(
role: "Prompt Contract Reviewer",
assignment: "# Target\npackages/coding-agent/src/prompts/**\n# Change\nCheck active-tool guidance. Shared contract: read-only; return mismatches and exact prompt lines.\n# Acceptance\nReturn confirmed findings only..."
assignment: "# Target\npackages/coding-agent/src/prompts/**\n# Change\nCheck active-tool guidance. Shared contract: read-only; return mismatches and exact prompt lines.\n# Acceptance\nReturn confirmed findings only…"
)
{{/if}}