Skip to content

Commit 01eeaac

Browse files
yeldarbyclaude
andcommitted
Fix 3 bugs and 1 rough-edge in auth and workspace handlers
Bug 1: Non-interactive login (--api-key) crashed because the API validation endpoint returns a welcome message, not workspace data. Now stores API key directly with workspace URL from the response. Bug 2: auth status failed after set-workspace because it read from get_conditional_configuration_variable which didn't see the config file updates. Now reads directly from the config file via _load_config. Bug 3: workspace get with invalid ID showed raw Python traceback. Now catches RoboflowError and shows a clean error message. Rough-edge: workspace get now shows human-readable text in non-JSON mode instead of dumping raw JSON. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5820b1c commit 01eeaac

File tree

2 files changed

+67
-31
lines changed

2 files changed

+67
-31
lines changed

roboflow/cli/handlers/auth.py

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -99,31 +99,35 @@ def _login(args: argparse.Namespace) -> None:
9999
force = getattr(args, "force", False)
100100

101101
if api_key:
102-
# Non-interactive: store key directly
102+
# Non-interactive: validate key and fetch workspace info
103103
import requests
104104

105105
from roboflow.config import API_URL
106106

107-
# Validate the key
108107
resp = requests.post(API_URL + "/?api_key=" + api_key)
109108
if resp.status_code == 401:
110109
output_error(args, "Invalid API key.", hint="Check your key at app.roboflow.com/settings", exit_code=2)
110+
return
111111
if resp.status_code != 200:
112112
output_error(args, f"API error ({resp.status_code}).", exit_code=1)
113+
return
113114

114115
r_login = resp.json()
115116
if r_login is None:
116117
output_error(args, "Invalid API key.", exit_code=2)
118+
return
119+
120+
# The validation endpoint returns {"workspace": "<url>", ...}
121+
ws_url = workspace_id or r_login.get("workspace", "")
122+
if not ws_url:
123+
output_error(args, "Could not determine workspace.", hint="Pass --workspace explicitly.", exit_code=1)
124+
return
117125

118-
config = {"workspaces": r_login}
119-
# Set default workspace
120-
first_ws_id = list(r_login.keys())[0]
121-
ws_url = r_login[first_ws_id]["url"]
122-
if workspace_id:
123-
# Verify requested workspace exists
124-
ws_by_url = {w["url"]: w for w in r_login.values()}
125-
if workspace_id in ws_by_url:
126-
ws_url = workspace_id
126+
# Build config with workspace info
127+
config = _load_config()
128+
workspaces = config.get("workspaces", {})
129+
workspaces[ws_url] = {"url": ws_url, "name": ws_url, "apiKey": api_key}
130+
config["workspaces"] = workspaces
127131
config["RF_WORKSPACE"] = ws_url
128132
_save_config(config)
129133

@@ -163,31 +167,39 @@ def _login(args: argparse.Namespace) -> None:
163167

164168
def _status(args: argparse.Namespace) -> None:
165169
from roboflow.cli._output import output, output_error
166-
from roboflow.config import get_conditional_configuration_variable
167170

168-
workspaces = get_conditional_configuration_variable("workspaces", default={})
169-
if not workspaces:
171+
config = _load_config()
172+
workspaces = config.get("workspaces", {})
173+
default_ws_url = config.get("RF_WORKSPACE")
174+
175+
if not workspaces and not default_ws_url:
170176
output_error(args, "Not logged in.", hint="Run 'roboflow auth login' to authenticate.", exit_code=2)
171177
return # unreachable, but helps mypy
172178

173-
workspaces_by_url = {w["url"]: w for w in workspaces.values()}
174-
default_ws_url = get_conditional_configuration_variable("RF_WORKSPACE", default=None)
175-
default_ws = workspaces_by_url.get(default_ws_url)
176-
177-
if not default_ws:
179+
if not default_ws_url:
178180
output_error(args, "No default workspace configured.", hint="Run 'roboflow auth set-workspace <id>'.")
179181
return # unreachable, but helps mypy
180182

181-
# Mask the API key
182-
masked = dict(default_ws)
183-
masked["apiKey"] = _mask_key(masked.get("apiKey", ""))
183+
workspaces_by_url = {w["url"]: w for w in workspaces.values()}
184+
default_ws = workspaces_by_url.get(default_ws_url)
184185

185-
lines = [
186-
f"Workspace: {masked.get('name', 'unknown')}",
187-
f" URL: {masked.get('url', 'unknown')}",
188-
f" API Key: {masked['apiKey']}",
189-
]
190-
output(args, masked, text="\n".join(lines))
186+
if default_ws:
187+
masked = dict(default_ws)
188+
masked["apiKey"] = _mask_key(masked.get("apiKey", ""))
189+
lines = [
190+
f"Workspace: {masked.get('name', 'unknown')}",
191+
f" URL: {masked.get('url', 'unknown')}",
192+
f" API Key: {masked['apiKey']}",
193+
]
194+
output(args, masked, text="\n".join(lines))
195+
else:
196+
# RF_WORKSPACE is set but no matching workspace details
197+
data = {"url": default_ws_url, "name": default_ws_url}
198+
output(
199+
args,
200+
data,
201+
text=f"Workspace: {default_ws_url}\n (no detailed info available)",
202+
)
191203

192204

193205
def _set_workspace(args: argparse.Namespace) -> None:

roboflow/cli/handlers/workspace.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,9 @@ def _list_workspaces(args: argparse.Namespace) -> None:
5151

5252
def _get_workspace(args: argparse.Namespace) -> None:
5353
from roboflow.adapters import rfapi
54+
from roboflow.adapters.rfapi import RoboflowError
5455
from roboflow.cli._output import output, output_error
55-
from roboflow.config import load_roboflow_api_key
56+
from roboflow.config import APP_URL, load_roboflow_api_key
5657

5758
workspace_id = args.workspace_id
5859
api_key = getattr(args, "api_key", None) or load_roboflow_api_key(workspace_id)
@@ -64,6 +65,29 @@ def _get_workspace(args: argparse.Namespace) -> None:
6465
hint="Run 'roboflow auth login' or pass --api-key.",
6566
exit_code=2,
6667
)
68+
return # unreachable, but helps mypy
6769

68-
workspace_json = rfapi.get_workspace(api_key, workspace_id)
69-
output(args, workspace_json)
70+
try:
71+
workspace_json = rfapi.get_workspace(api_key, workspace_id)
72+
except RoboflowError:
73+
output_error(
74+
args,
75+
f"Workspace '{workspace_id}' not found.",
76+
hint=f"Check the workspace ID and try again. Browse workspaces at {APP_URL}.",
77+
exit_code=3,
78+
)
79+
return # unreachable, but helps mypy
80+
81+
# Human-readable text for non-JSON mode
82+
ws = workspace_json.get("workspace", workspace_json)
83+
name = ws.get("name", workspace_id)
84+
members = ws.get("members", {})
85+
projects = ws.get("projects", [])
86+
lines = [
87+
f"Workspace: {name}",
88+
f" URL: {workspace_id}",
89+
f" Link: {APP_URL}/{workspace_id}",
90+
f" Members: {len(members)}",
91+
f" Projects: {len(projects)}",
92+
]
93+
output(args, workspace_json, text="\n".join(lines))

0 commit comments

Comments
 (0)