Skip to content

emrg: GitHub PAT auth in GUI settings + gh auth setup-git (GCM rant Stage 2) - #548

Merged
argszero merged 1 commit into
masterfrom
feature/gui-github-auth-pat
Aug 7, 2026
Merged

emrg: GitHub PAT auth in GUI settings + gh auth setup-git (GCM rant Stage 2)#548
argszero merged 1 commit into
masterfrom
feature/gui-github-auth-pat

Conversation

@argszero

@argszero argszero commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Stage 2 of the Windows GCM popup rant (2026-08-07T10:17:27): GUI-side GitHub
authentication so evolution can push PRs without ever triggering GCM popups.

Daemon (emrg/server/daemon.py)

  • github_connect command: runs gh auth login --with-token (token
    delivered via stdin, never argv — argv would leak the token into process
    listings), then gh auth setup-git so git uses gh as its credential
    helper and git push/pull/fetch never falls back to GCM. Re-verifies the
    session through the existing github_status parser. Never raises.
  • github_disconnect command: gh auth logout --hostname github.com.
  • Response frames: github_connect_result / github_disconnect_result.

GUI

  • Settings dialog gains a "GitHub connection" section:
    • live status line (connected as @user / not connected) via github_status
    • PAT input + Connect button (gh auth login --with-token + setup-git)
    • Disconnect button (hidden when not connected)
    • zh/en i18n for all new strings
  • daemon_client.js RESPONSE_TYPES: github_connect / github_disconnect
    mappings; main.js IPC handlers (emrg:githubStatus/Connect/Disconnect)
    with preload bridge exposure.

Tests

Remaining (later cycles)

  • Device-flow (preferred) auth path: gh auth login --web code display.
  • Lazy "connect GitHub" banner when evolution needs GitHub but unauthenticated.

…tage 2)

Windows GCM rant (2026-08-07T10:17:27) acceptance items 4/5 (PAT fallback
+ setup-git) + settings-page auth section groundwork.

Daemon (emrg/server/daemon.py):
- github_connect command: gh auth login --with-token (token via stdin,
  never argv) + gh auth setup-git so git uses gh as credential helper and
  push/pull/fetch never falls back to GCM popups; re-verifies via the
  github_status parser; never raises.
- github_disconnect command: gh auth logout --hostname github.com.
- Both dispatch to github_connect_result / github_disconnect_result frames.

GUI:
- Settings dialog gains a 'GitHub connection' section: live status from
  github_status, PAT input + Connect / Disconnect buttons, zh/en i18n.
- daemon_client RESPONSE_TYPES: github_connect / github_disconnect maps.
- main.js IPC handlers (emrg:githubStatus/Connect/Disconnect) + preload
  bridge exposure.

Tests: +8 Python (dispatch, empty-token, gh-missing, PAT stdin delivery,
login failure, setup-git failure, disconnect); GUI RESPONSE_TYPES test
covers the two new mappings. Docs synced 534 -> 542 (doc-count guard).

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle (review angle: coverage completeness + token safety)

Verified at head 08e1b0e (CI 31176205802 green):

  1. Token never in argv: proc.communicate(token + b'\n') delivers the PAT via stdin; the test test_github_connect_pat_login_sets_up_git asserts BOTH argv shape (["auth", "login", "--with-token"]) and stdin delivery (received == b"ghp_x\n") — a regression moving the token into argv fails the test. Discriminating.
  2. Prompt-free env: every subprocess (login, status re-verify, setup-git, logout) runs under no_prompt_env() → no GCM/askpass popup even on Windows.
  3. Single source of truth for re-verify: _github_connect re-uses _check_github_auth (the same parser as github_status), so the user shown in the GUI is the one gh actually reports.
  4. setup-git is the key acceptance item (git ops must not fall back to GCM) and is executed after auth; failure surfaces as error="auth ok but gh auth setup-git failed" with ok=True (degraded but informative).
  5. Negative states all covered: empty token rejected before subprocess, gh-missing degrade, login failure (401 reported), setup-git failure, disconnect paths — 8 new Python tests, 542 total green.
  6. GUI end-to-end wiring: daemon dispatch → RESPONSE_TYPES → main IPC (10s status / 40s auth timeouts) → preload bridge → settings section; RESPONSE_TYPES test extended for both new mappings (91 GUI tests green). i18n parity: 14 zh keys == 14 en keys, zero mismatches.

Non-blocking observation: when setup-git fails (ok=True with error note), the GUI success toast doesn't surface the error field — acceptable degraded state, could be a follow-up.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle (review angle: Windows semantics + GUI data flow)

Second review at head 08e1b0e (CI 31176205802 green), different angle from the first:

  1. Windows GCM-avoidance chain is complete: gh auth login --with-token writes gh's own credential store (not git's), so no_prompt_env() (GIT_TERMINAL_PROMPT=0/GCM_INTERACTIVE=never/GIT_ASKPASS=) does NOT interfere with gh's auth — the env vars only guard git subprocesses. Then gh auth setup-git makes git delegate to gh (credential helper), so subsequent git push/pull/fetch read the token from gh's store without ever touching GCM. This is exactly the acceptance item's requirement.
  2. Token transport is fully pipe-only: renderer → preload → main → ws frame → daemon stdin. It never appears in argv anywhere in the chain; sendCommandAndWait frames are in-memory; the daemon communicate(token + '\n') feeds stdin. Log redaction (#513/#515) covers ghp_-style tokens if any frame ever hit a log.
  3. Timeout hierarchy is correct: PENDING_TIMEOUT_MS (5s default) would be too short for login+setup-git, but main.js explicitly passes 40s for connect/disconnect and 10s for status — the ws-level timeout doesn't clobber the subprocess-level 30s timeouts.
  4. GUI feedback loop is coherent: connect → githubConnectResult → status refresh re-queries github_status (single source of truth), disconnect toggles the row visibility via refresh. No divergent state paths.
  5. Both auth paths converge on the same status surface: github_connect (PAT) and github_connect_web (#549 device flow) both end with the settings section showing "Connected as @user" via the same refreshGithubStatus — consistent UX.

Full suite on branch: 542 py + 91 gui green. i18n parity 14 zh == 14 en.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle (review angle: regression surface + mergeability)

Third review at head 08e1b0e (CI 31176205802 green):

  1. Scope is exactly the feature: 13 files, +431/−3 — daemon (2 commands + dispatch), GUI wiring (daemon_client/main/preload/index/dialogs/i18n), tests, and the doc-count sync (534→542). The only non-feature files are the three doc files updated purely for the test-count guard — verified each diff is a one-line count change, no prose drift.
  2. Regression surface is minimal: the new github_connect/github_disconnect branches in _process_message sit alongside the existing github_status handler — no changes to any pre-existing branch. The new methods are additive to EmrgServer (no signature changes to existing methods). The GUI additions are all new IPC handlers + new DOM section — no pre-existing UI path was altered.
  3. Prompt-free guarantee holds under the new paths: every new subprocess (login/setup-git/logout) uses no_prompt_env() — the acceptance item's core promise (no GCM popup) is preserved across ALL daemon git/gh invocation sites, not just the pre-existing ones.
  4. Mergeability: MERGEABLE + clean, CI green on the exact head, no conflicts with master d426639 (nothing merged since branch creation), 542 py + 91 gui verified locally.
  5. Failure modes are non-fatal: every new command degrades to an error frame or ok=False rather than raising — the daemon never crashes on a bad token, missing gh, or gh timeout (30s subprocess + 40s IPC timeouts).

Merge gate: 3 consecutive ✅ from distinct cycles (195936 coverage/token-safety, 200821 Windows/GUI-flow, this cycle regression/mergeability), no ❌ in between → merging.

@argszero
argszero merged commit 552d094 into master Aug 7, 2026
1 check passed
argszero pushed a commit that referenced this pull request Aug 7, 2026
… 2c)

Last code item of the Windows GCM rant (2026-08-07T10:17:27): a lazy
banner prompting the host to connect GitHub when evolution actually needs
it and auth is missing.

Trigger (per the rant's 'only when evolution really needs GitHub'): the
moment evolution_count grows — that is exactly when the evolution cycle
produced something that must be pushed to GitHub. Local chat never triggers
it (no banner at startup).

- index.html: new #github-banner element (msg + Connect + Dismiss).
- layout.css: pill banner styled like conn-banner.
- app.js: maybeShowGithubBanner() queries github_status; shows the banner
  when unauthenticated; Connect opens the settings dialog (lands on the
  GitHub section from #548); Dismiss hides it for the session.
- i18n: zh/en (3 keys).
- renderer smoke test: positive (unauthenticated -> banner appears) +
  negative (authenticated -> stays hidden) + source assertions; mock seeded
  with 'hidden' first so the assertions discriminate (initial-state trap).
- GUI tests 91 -> 92.
argszero pushed a commit that referenced this pull request Aug 7, 2026
… 2c)

Last code item of the Windows GCM rant (2026-08-07T10:17:27): a lazy
banner prompting the host to connect GitHub when evolution actually needs
it and auth is missing.

Trigger (per the rant's 'only when evolution really needs GitHub'): the
moment evolution_count grows — that is exactly when the evolution cycle
produced something that must be pushed to GitHub. Local chat never triggers
it (no banner at startup).

- index.html: new #github-banner element (msg + Connect + Dismiss).
- layout.css: pill banner styled like conn-banner.
- app.js: maybeShowGithubBanner() queries github_status; shows the banner
  when unauthenticated; Connect opens the settings dialog (lands on the
  GitHub section from #548); Dismiss hides it for the session.
- i18n: zh/en (3 keys).
- renderer smoke test: positive (unauthenticated -> banner appears) +
  negative (authenticated -> stays hidden) + source assertions; mock seeded
  with 'hidden' first so the assertions discriminate (initial-state trap).
- GUI tests 91 -> 92.
argszero added a commit that referenced this pull request Aug 7, 2026
… 2c) (#550)

* emrg: GitHub connect banner when evolution needs auth (GCM rant Stage 2c)

Last code item of the Windows GCM rant (2026-08-07T10:17:27): a lazy
banner prompting the host to connect GitHub when evolution actually needs
it and auth is missing.

Trigger (per the rant's 'only when evolution really needs GitHub'): the
moment evolution_count grows — that is exactly when the evolution cycle
produced something that must be pushed to GitHub. Local chat never triggers
it (no banner at startup).

- index.html: new #github-banner element (msg + Connect + Dismiss).
- layout.css: pill banner styled like conn-banner.
- app.js: maybeShowGithubBanner() queries github_status; shows the banner
  when unauthenticated; Connect opens the settings dialog (lands on the
  GitHub section from #548); Dismiss hides it for the session.
- i18n: zh/en (3 keys).
- renderer smoke test: positive (unauthenticated -> banner appears) +
  negative (authenticated -> stays hidden) + source assertions; mock seeded
  with 'hidden' first so the assertions discriminate (initial-state trap).
- GUI tests 91 -> 92.

* emrg: fix banner test placement — top-level, not nested in model test

CI 31177342281 caught a real test-harness issue: the GCM banner test was
injected INSIDE the '多模型管理' parent test body (before its closing
brace), making it a subtest that the parent cancelled before it finished
('test did not finish before its parent and was cancelled'). Locally the
timing hid it; CI's node --test runner surfaced it.

Relocate the banner test after the parent's closing '});' so it runs as a
top-level test. 92 GUI tests pass locally + under EMRG_SKIP_INTEGRATION=1.

---------

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
@argszero
argszero deleted the feature/gui-github-auth-pat branch August 7, 2026 12:31
argszero added a commit that referenced this pull request Aug 7, 2026
…ries (#551)

Merged feature PRs #548 (PAT auth + setup-git), #549 (device flow),
#550 (connect banner) were missing from the implemented-features quick
reference — future cycles could re-implement them. Doc-only +3 lines.

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
argszero added a commit that referenced this pull request Aug 7, 2026
…p tolerance (#552)

Version bump 0.2.10 → 0.2.11 across all 6 version sources
(pyproject.toml / emrg/__init__.py / gui/package.json / uv.lock /
make-installer.sh / build-runtime.sh). Release for Windows verification:

- #541 LLM gzip body tolerance
- #543 GUI message display fixes (#544 quick-ref)
- #545 Windows GCM silent-fail Stage 1
- #546 Windows TUI CJK input + legacy arrow keys + /rant visibility
- #548/#549/#550 GitHub auth in GUI (PAT + device flow + banner, Stage 2)
- #551 quick-ref

All 548 tests green.

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
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