Skip to content

fix(dashboard): stop harness Connect opening two browser tabs, restore Settings icons - #786

Merged
aaronjmars merged 2 commits into
mainfrom
fix/harness-connect-double-tab
Jul 27, 2026
Merged

fix(dashboard): stop harness Connect opening two browser tabs, restore Settings icons#786
aaronjmars merged 2 commits into
mainfrom
fix/harness-connect-double-tab

Conversation

@aaronjmars

Copy link
Copy Markdown
Collaborator

Two dashboard fixes found while running the app locally.

Connect opened the auth page twice

The grok, codex and kimi connect routes each parsed the verification URL out of the CLI's live output and opened it themselves. The grok route's comment recorded that as necessary because grok "prints the verification URL and waits rather than opening the browser itself" (noted as confirmed on grok 0.2.82), and the codex/kimi helper documented its own open as a deliberate fallback.

Neither holds. All three CLIs open the URL themselves, so every Connect landed the operator on two identical auth tabs. From the default browser's tab list:

device?user_code=YJMX-Y6RY   <- bare `grok login --device-auth`: 1 tab (grok self-opens)
device?user_code=TX2F-8ZAE   <- the same flow via the dashboard route
device?user_code=TX2F-8ZAE   <- same code, second tab

Same for the siblings: codex login took the browser from 7 to 8 tabs on its own, and kimi login prints Opening browser for Kimi device login. Verified on grok 0.2.106.

The routes now wait on the tab the CLI opened. They still parse the URL, but only to quote back in the timeout error so an operator whose browser never opened can finish by hand. That parse is also no longer latched to the first matching chunk, which could pin a truncated link when a chunk boundary fell mid-URL.

openBrowser stays in use for MCP OAuth, where the dashboard builds the authorize URL itself and no CLI is involved.

Tradeoff worth naming: if a CLI ever fails to open a browser, the URL now surfaces only when the 240s timeout fires rather than immediately. That is the cost of removing the guaranteed duplicate; showing it sooner would mean streaming the URL back mid-request, a larger change. The dashboard is explicitly a local-machine tool, so the CLI's own open is the right primary.

Seven credentials had no icon in Settings

CODEX_AUTH, KIMI_AUTH, OPENAI_API_KEY, MOONSHOT_API_KEY, MISTRAL_API_KEY, GH_READ_PAT and GH_SECRETS_PAT were added to lib/secrets-catalog.ts without a matching row in the service-icon map, so each fell through to the grey two-letter initials badge.

Each favicon was checksummed against a 404 control and visually inspected before being wired, so none of them is the service's generic fallback mark.

Those are two separate lists that nothing diffed, which is exactly how they drifted. Icon resolution is now an exported resolveServiceMark, and a new lib/service-icon.test.ts asserts every catalogued secret earns a logo or glyph, failing with the offending name:

AssertionError: secrets missing an icon: GH_READ_PAT

Confirmed the guard bites by deleting an entry and watching it fail. Custom (non-catalog) secrets still fall back to initials by design, which the test pins too.

Verification

Checked in the running dashboard, not just in tests: Connect now opens exactly one tab, and a DOM sweep of Settings returns zero initials badges with every new <img> loading (naturalWidth > 0).

npm run typecheck, npm test (169 pass) and npm run build are clean in this tree; knip reports no new unused exports.

…e Settings icons

Two dashboard fixes found while running the app locally.

Connect opened the auth page twice. The grok, codex and kimi connect routes
each parsed the verification URL out of the CLI's live output and opened it
themselves. The grok route's comment recorded that as necessary because grok
"prints the verification URL and waits rather than opening the browser itself"
(noted as confirmed on grok 0.2.82), and the codex/kimi helper documented its
own open as a deliberate fallback. Neither holds: all three CLIs open the URL
themselves, so every Connect produced two identical auth tabs. Confirmed by
tab-list inspection - a bare "grok login --device-auth" opens one tab, the same
flow through the route opened two with the same user_code; "codex login" takes
the browser from 7 to 8 tabs; "kimi login" prints "Opening browser for Kimi
device login". Verified on grok 0.2.106.

The routes now wait on the tab the CLI opened. They still parse the URL, but
only to quote back in the timeout error so an operator whose browser never
opened can finish by hand. That parse is also no longer latched to the first
matching chunk, which could pin a truncated link when a chunk boundary fell
mid-URL. openBrowser stays in use for MCP OAuth, where the dashboard builds the
authorize URL itself and no CLI is involved.

Seven credentials had no icon in Settings. CODEX_AUTH, KIMI_AUTH,
OPENAI_API_KEY, MOONSHOT_API_KEY, MISTRAL_API_KEY, GH_READ_PAT and
GH_SECRETS_PAT were added to lib/secrets-catalog.ts without a matching row in
the service-icon map, so each fell through to the grey two-letter initials
badge. Each favicon was checksummed against a 404 control and inspected before
being wired, so none is the service's generic fallback mark.

Those are two separate lists that nothing diffed, which is how they drifted.
Icon resolution is now an exported resolveServiceMark, and a new
lib/service-icon.test.ts asserts every catalogued secret earns a logo or glyph,
failing with the offending secret's name. Confirmed the guard bites by removing
an entry and watching it fail.

Verified in the running dashboard: Connect now opens exactly one tab, and no
initials badge remains in Settings. typecheck, test (169 pass) and build are
clean; knip reports no new unused exports.
…checks

The cli typecheck job compiles ../dashboard/lib/**/*.ts with no jsx option, so
the new lib/service-icon.test.ts importing the ServiceIcon component failed the
build with TS6142 ("was resolved to ServiceIcon.tsx, but --jsx is not set").
The dashboard's own typecheck passed, which is why this only showed up in CI.

Split the resolution data and resolveServiceMark into a plain
lib/service-icons.ts, leaving ServiceIcon.tsx with the rendering (glyph paths,
initials, chip markup). Same shape lib/harness-auth.ts already uses to stay
importable from both client components and node. The test now imports the lib
module, so nothing under lib/ reaches for a .tsx.

Reproduced the failing job locally before and after: it errors on the old
import and is clean on the new one. Dashboard typecheck, test (169 pass) and
build stay green, and the Settings panel still renders zero initials badges
with every icon loading.
@aaronjmars
aaronjmars force-pushed the fix/harness-connect-double-tab branch from 2326b85 to 340d37b Compare July 27, 2026 18:43
@aaronjmars
aaronjmars merged commit 19b033c into main Jul 27, 2026
4 checks passed
@aaronjmars
aaronjmars deleted the fix/harness-connect-double-tab branch July 27, 2026 18:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant