Repository navigation
Team picker: switch/create teams and scope Cloud #13051
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
3257108
feat: add team picker account menu and shared team scope
austinywang b313d39
test: align dashboard team scope with Stack selection
austinywang 04c52bb
fix: fence concurrent team mutations and async socket actions
austinywang 0b59445
chore: normalize team picker localization diff
austinywang 29c2783
docs: clarify Stack team scope authority
austinywang 6147c4a
fix: keep team socket status fully asynchronous
austinywang ce8a0ea
Merge remote-tracking branch 'origin/main' into 13019-team-picker-swi…
austinywang f2df323
fix: fence VM attempts across team switches
austinywang 8ee7488
fix: localize team picker shortcut descriptions
austinywang c4cc2d5
fix: expose public team selection result
austinywang c0511d0
fix: allow coordinator team refresh publication
austinywang 3401e61
fix: import team picker socket dependencies
austinywang 8460aa0
fix: return observed account flow values
austinywang a0ccc35
fix: import sidebar popover anchor
austinywang ec05ca2
fix: keep original compact account menu styling
austinywang 9d3c871
fix: keep team checkmark neutral
austinywang ca047a1
fix: create teams through authenticated cmux backend
austinywang 1b92bf1
fix: keep stack client initializer public API stable
austinywang 89e4087
fix: align account team picker settings row
austinywang de97136
fix: serialize complete reloads for each development tag
austinywang 9c1858a
fix: preserve hover team submenus and dismiss the menu together
austinywang c0a935c
Merge main and normalize account menu spacing
austinywang f04a377
fix: tighten account menu section spacing
austinywang 0cfbacf
fix: anchor team submenu to menu edge
austinywang cf0abff
fix: keep account popover open while hovering team submenu
austinywang 5a669b8
fix: track mouse movement over account popovers
austinywang abbf8d7
Merge origin/main and resolve current CI conflicts
austinywang bf27830
test: cover popover hover tracking lifecycle
austinywang b2037b8
Merge remote-tracking branch 'origin/main' into 13019-team-picker-swi…
austinywang 0d0a49c
fix: close team submenu when pointer leaves its hover region
austinywang a44540b
Merge branch 'main' of https://github.com/manaflow-ai/cmux into 13019…
austinywang 6d33af4
Merge origin/main and preserve team scope fencing with resource stats
austinywang File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| import Foundation | ||
|
|
||
| extension CMUXCLI { | ||
| /// Implements `cmux auth team` through the same authenticated socket | ||
| /// actions used by the sidebar picker. | ||
| func runAuthTeamCommand( | ||
| commandArgs: [String], | ||
| client: SocketClient, | ||
| jsonOutput: Bool | ||
| ) throws { | ||
| let subcommand = commandArgs.first?.lowercased() ?? "list" | ||
| switch subcommand { | ||
| case "list": | ||
| let response = try client.sendV2(method: "auth.team.list") | ||
| if jsonOutput { | ||
| print(jsonString(response)) | ||
| } else { | ||
| let teams = response["teams"] as? [[String: Any]] ?? [] | ||
| let selected = response["selected_team_id"] as? String | ||
| for team in teams { | ||
| guard let id = team["id"] as? String else { continue } | ||
| let name = team["display_name"] as? String ?? id | ||
| print("\(id == selected ? "*" : " ") \(name) (\(id))") | ||
| } | ||
| } | ||
| case "use": | ||
| guard commandArgs.count == 2, !commandArgs[1].isEmpty else { | ||
| throw CLIError(message: String( | ||
| localized: "cli.auth.team.useUsage", | ||
| defaultValue: "Usage: cmux auth team use <team-id>" | ||
| )) | ||
| } | ||
| let response = try client.sendV2( | ||
| method: "auth.team.use", | ||
| params: ["team_id": commandArgs[1]] | ||
| ) | ||
| if jsonOutput { | ||
| print(jsonString(response)) | ||
| } else { | ||
| let selected = response["selected_team_id"] as? String ?? commandArgs[1] | ||
| print(String(format: String( | ||
| localized: "cli.auth.team.selected", | ||
| defaultValue: "Selected team: %@" | ||
| ), selected)) | ||
| } | ||
| case "create": | ||
| let displayName = commandArgs.dropFirst().joined(separator: " ") | ||
| .trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard !displayName.isEmpty else { | ||
| throw CLIError(message: String( | ||
| localized: "cli.auth.team.createUsage", | ||
| defaultValue: "Usage: cmux auth team create <name>" | ||
| )) | ||
| } | ||
| let response = try client.sendV2( | ||
| method: "auth.team.create", | ||
| params: ["display_name": displayName] | ||
| ) | ||
| if jsonOutput { | ||
| print(jsonString(response)) | ||
| } else { | ||
| let selected = response["selected_team_id"] as? String ?? displayName | ||
| print(String(format: String( | ||
| localized: "cli.auth.team.created", | ||
| defaultValue: "Created and selected team: %@" | ||
| ), selected)) | ||
| } | ||
| default: | ||
| throw CLIError(message: String( | ||
| localized: "cli.auth.team.usage", | ||
| defaultValue: "Usage: cmux auth team <list|use <team-id>|create <name>>" | ||
| )) | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
...d/CmuxAuthRuntime/Sources/CmuxAuthRuntime/Coordinator/AuthCoordinator+TeamSelection.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| public import CMUXAuthCore | ||
|
|
||
| public extension AuthCoordinator { | ||
| /// Persist a team selection on Stack Auth before changing the local | ||
| /// projection. Every UI surface uses this action so a rejected request | ||
| /// leaves the current cloud scope and open work untouched. | ||
| /// - Parameter id: A team id from ``availableTeams``. | ||
| func selectTeam(id: String?) async throws { | ||
| if let id, !availableTeams.contains(where: { $0.id == id }) { | ||
| throw AuthClientError.teamNotAvailable | ||
| } | ||
| teamMutationGeneration &+= 1 | ||
| let mutationGeneration = teamMutationGeneration | ||
| let sessionGeneration = self.sessionGeneration | ||
| try await client.setSelectedTeam(id: id) | ||
| guard sessionGeneration == self.sessionGeneration, | ||
| mutationGeneration == teamMutationGeneration, | ||
| isAuthenticated else { | ||
| throw AuthError.unauthorized | ||
| } | ||
| selectedTeamID = id | ||
| } | ||
|
|
||
| /// Creates a team, refreshes membership, and selects the new team. | ||
| /// - Parameter displayName: The display name entered by the user. | ||
| /// - Returns: The authoritative newly-created team. | ||
| func createTeam(displayName: String) async throws -> CMUXAuthTeam { | ||
| let trimmed = displayName.trimmingCharacters(in: .whitespacesAndNewlines) | ||
| guard !trimmed.isEmpty else { throw AuthClientError.invalidTeamName } | ||
| guard isAuthenticated else { throw AuthError.unauthorized } | ||
| teamMutationGeneration &+= 1 | ||
| let mutationGeneration = teamMutationGeneration | ||
| let generation = sessionGeneration | ||
| let created = try await client.createTeam(displayName: trimmed) | ||
| guard generation == sessionGeneration, | ||
| mutationGeneration == teamMutationGeneration, | ||
| isAuthenticated else { | ||
| throw AuthError.unauthorized | ||
| } | ||
| var refreshed = try await client.listTeams() | ||
| guard generation == sessionGeneration, | ||
| mutationGeneration == teamMutationGeneration, | ||
| isAuthenticated else { | ||
| throw AuthError.unauthorized | ||
| } | ||
| if !refreshed.contains(where: { $0.id == created.id }) { | ||
| refreshed.append(created) | ||
| } | ||
| availableTeams = refreshed | ||
| try await selectTeam(id: created.id) | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| return created | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.