Commit graph

2 commits

Author SHA1 Message Date
Johannes du Plessis
cb4c643e43
feat: open Slack-triggered PRs as the triggering user (#1375)
* feat: open Slack-triggered PRs as the triggering user

Route the Slack per-user GitHub token through the dashboard OAuth store
(the backend the self-service link prompt populates) and block runs that
lack a valid user token, prompting the user to (re-)link. Per-user OAuth
now wins over bot-token-only mode for mapped Slack/dashboard users.

Flip commit/PR authorship across all sources: the triggering user is the
commit author (via repo-local git identity using their resolvable GitHub
noreply email) and open-swe[bot] is the Co-authored-by collaborator.

* fix: address PR review — shell-escape commit identity, fix token cache impersonation

- Shell-escape the triggering user's name/email with shlex.quote before
  embedding them in the repo-setup `git config` command, so a name like
  O'Connor (or a crafted one) can't break or inject into the command.
- Stop consulting the shared thread-metadata token cache in
  _resolve_dashboard_user_token. Slack thread ids are shared across the
  conversation, so a cached token from a prior triggering user could be
  returned for the current github_login. Always resolve by login from the
  dashboard OAuth store instead.

* feat: dashboard self-service user mapping + UI cleanup

- Add session-scoped GET/PUT /dashboard/api/my-mapping so users can set their
  own work email / Slack member ID (keyed by their GitHub login, source=self).
- Slack account-link prompt now redirects to Profile Settings after auth.
- Rename "My Settings" -> "Profile Settings" and "Cloud Agents" -> "Open SWE
  Agent"; remove the Integrations tab/section (folded out, low value for now)
  and redirect /integrations to Profile Settings.
- Add a "User mapping" section to Profile Settings (work email used by Slack
  and Linear, optional Slack member ID).
- Make dashboard auth cookies scheme-aware: Secure;SameSite=None over HTTPS,
  non-Secure;SameSite=Lax over http://localhost so local login works.

* feat: self-service Slack account linking via Sign in with Slack (OIDC)

Replace the spoofable manual work-email/Slack-ID form with a verified
"Sign in with Slack" flow so a logged-in GitHub user can only ever link
their own Slack identity.

- New agent/dashboard/slack_oauth.py: OIDC authorize URL, code exchange,
  userInfo identity parse, optional workspace gate, configured check.
- routes.py: session-gated GET /slack/login and /slack/callback that upsert
  the mapping from Slack-verified user_id + email (source=slack_oauth).
  Remove the spoofable PUT /my-mapping; expose slack_oauth_enabled on /me.
- UI: drop the editable inputs; add a Connect Slack button + status to the
  User mapping section.

Admin-managed mappings are unaffected and still resolve at trigger time.
2026-06-02 15:04:20 -07:00
Johannes du Plessis
1ea9c0d880
feat: refactor UI into sidebar layout (#1318)
* feat(dashboard): refactor UI into Cursor-style sidebar layout

Replace the top-bar AppHeader with a left sidebar that holds the four
primary sections (My Settings, Cloud Agents, Open SWE Review,
Integrations) and the user account menu at the bottom. Admin remains a
hidden route reachable from the sidebar only when is_admin.

UI:
- AppShell + AppSidebar with bottom-anchored avatar/sign-out menu.
- My Settings: profile (first/last name) + PR destination preference.
- Cloud Agents: model/effort, default repo, base branch, branch prefix,
  PR toggles (auto-fix CI, create PRs, allow artifacts), Slack
  notifications.
- Open SWE Review: trigger mode, draft reviews, PR summaries, autofix
  mode + severity threshold, plus the existing per-repo review-style
  prompts (was /review-styles).
- Integrations: GitHub install status + Slack/Linear status rows.
- New Switch and Menu primitives.

Backend:
- Extend ProfileUpdate with first_name, last_name, base_branch,
  branch_prefix, auto_fix_ci, create_prs, allow_artifacts,
  slack_notifications, preferred_pr_destination.
- Add team_settings module + /team-settings GET (any session) / PUT
  (admin only) for the reviewer configuration.

* fix(dashboard): replace base-ui Menu with plain dropdown, stabilise Selects

- base-ui's Menu hit an "Invalid hook call / Cannot read properties of
  null (reading 'useRef')" crash inside fastComponent under Vite's dep
  optimisation. The sidebar account menu is the only consumer, so swap
  it for a useState-driven dropdown with click-outside + Esc handling
  and drop the unused Menu wrapper.
- Initialise the Open SWE Review settings form with the same defaults
  the server returns instead of `null`, so the Selects don't switch
  from uncontrolled (`undefined`) to controlled on first data load.

* fix(dashboard): address review feedback on admin overwrite + form reset

- admin PUT /admin/profiles/{login} only sent model/effort/repo from the
  admin form. ProfileUpdate's Pydantic defaults then wrote auto_fix_ci /
  create_prs / slack_notifications etc back onto the target user's
  profile, clobbering whatever they had configured. Switch to
  model_dump(exclude_unset=True) and overlay the incoming fields on the
  user's existing stored profile so only sent fields change.

- my-settings.tsx and cloud-agents.tsx initialised local state from
  profile.data on every change. Because useSaveProfile updates the
  cached profile on success, toggling any control would overwrite the
  user's typed-but-unsaved input in another field. Guard the
  initialisation effect with a ref so it runs once on first load.

---------

Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
2026-05-20 14:13:23 -07:00