feat(api-keys): roboflow api-key CLI#495
Conversation
Checkpoint of the API key management feature work (round 1 + refinement pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… hint Round-2 refinement fixes (independent re-review). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Emit the create result (incl. the one-time secret) via the shared output() helper like every other handler, instead of a raw print(). Resolves a CodeQL clear-text-logging alert and matches the repo's documented output idiom; stdout is unchanged in both --json and human modes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…sets Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… + presets External review (P2): omitting scopes inherits the caller's scopes (not unconditional full access); note role:<name> presets and [] for no abilities. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
External review (skills PR): the command-groups reference omitted api-key, so agents couldn't discover it. The group ships in this PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
digaobarbosa
left a comment
There was a problem hiding this comment.
LGTM after API merge
Explain the create privilege (unscoped or api-key:create; OAuth also needs create_api_key) and the all-folder requirement for workspace-wide keys. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Was looking at this PR and noticed a few things — posting in case any of it's useful. I'm not affiliated with Roboflow, just an outside contributor who happened to dig in. CI failure is repo-wide, not from this PRAll 8 The failure is in numpy's type stub file, not in any file this PR touches. This looks like a numpy 2.5.0 × mypy interaction — numpy 2.5.0 ships stubs using the Python 3.12+ To confirm it's repo-wide: the same failure hits an unrelated PR the same day — the Likely fixes (for maintainers, not this PR): pin Likely bug:
|
| Line | Key | Convention |
|---|---|---|
| 124 | continueIfNoRefund |
camelCase |
| 290 | annotationGroup |
camelCase |
| 413 | removeMetadata |
camelCase |
| 415 | addTags |
camelCase |
| 684 | batchName |
camelCase |
| 1519 | folderIds |
camelCase |
| 1521 | custom_metadata |
snake_case ← outlier |
The backend almost certainly expects customMetadata to match folderIds in the same request. If so, metadata is silently dropped on every api-key create call that passes --metadata.
The same flows through _update_key at roboflow/cli/handlers/api_key.py:349 (fields["custom_metadata"]), and the update_api_key docstring at rfapi.py:1534 references custom_metadata.
No test catches this because every existing test mocks at the Python function level (mock_create.assert_called_once_with(...)) and never inspects what requests.post actually sends. The TestStatusCodePropagation class already mocks requests.* but only checks status_code, not the body.
Suggested fix: body["custom_metadata"] → body["customMetadata"] at rfapi.py:1521, and fields["custom_metadata"] → fields["customMetadata"] at api_key.py:349. Plus a test that mocks requests.post and asserts call_args.kwargs["json"] contains customMetadata (camelCase).
Caveat: I'm inferring customMetadata from the convention of every sibling key. The backend owner should confirm the exact field name before merging.
Minor: error hint accuracy
A few of the error hints are broader than they should be. Not blockers, but worth a look:
_create_key (api_key.py:296–316): if status in (403, 404) gives the same hint for both. A 404 means "workspace or endpoint not found", not "you lack the api-key:create scope" — the scope hint is misleading for 404. The else branch gives a plan-feature hint for every other status including 401 (auth), which is confusing. Suggested: split 403 and 404, and only apply the plan-feature hint for 403.
_update_key (api_key.py:351–359): doesn't special-case 403 (plan feature) or 409 (protected key) the way _disable_key does (api_key.py:401–422). A user hitting api-key update <id> --scope image:read without the plan feature gets a generic error with no "Advanced API Keys" hint.
_protect_key (api_key.py:375–382): passes the "The API cannot unprotect a key..." hint for every error including 500s and 401s. That hint only makes sense for 403. Suggested: scope it to 403 only.
Minor: missing CHANGELOG + CLI-COMMANDS.md examples
roboflow/__init__.py:24bumps the version to1.3.11, butCHANGELOG.mdhas no## 1.3.11section (every prior release has one).CLI-COMMANDS.mdgot a table row forapi-key(line 360) but no usage examples subsection — every other command group has one (e.g.### Inspect model evaluationsat line 245).
What's already good
- All 42 tests in
tests/cli/test_api_key.pypass locally. ruff checkandruff format --checkare clean on the PR's files.- Exit-code contract (0/1/2/3) is correctly implemented and tested.
--jsonoutput and destructive-confirmation gating are correct.- Handler registration, alphabetical ordering, and
ctx_to_argsbridge all follow the repo's conventions.
Hope this is helpful — happy to elaborate on any of it.
Adds --no-scopes ([]), --full-access (explicit null/unscoped), and --clear-metadata ({}) to 'roboflow api-key create/update', mutually exclusive with --scope/--metadata. Adds a backward-compatible FULL_ACCESS sentinel to rfapi.py so create_api_key/update_api_key can emit an explicit null scopes value (previously None only meant omit). 18 new CLI tests; full CLI suite green, ruff + mypy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a
roboflow api-keyCLI command group (list / get / create / update / revoke / protect / disable / publishable) wrapping the new REST endpoints.keyIdhandles;--scope/--metadataon create + update; protected-key 409 and plan-feature 403 hints; consistent exit codes and--jsonenvelopes.tests/cli/test_api_key.py; version bump.🤖 Generated with Claude Code