fix(cli): reuse @agent-relay/cloud auth as Bearer; drop workspace-token mint#113
Conversation
…er 403 hint When agentworkforce login is invoked with --workspace, listWorkspacesForLogin should not be called — but the previous flow always listed first and only short-circuited the picker. That meant users hitting 403 on /api/v1/workspaces could not log in at all, even with the workspace id in hand. This PR: - Short-circuits listWorkspacesForLogin when opts.workspace is provided - Surfaces a clearer error when the list 403s, pointing at --workspace - Surfaces a clearer error when the list is empty (no workspaces yet) - Adds tests covering all three paths Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…en mint
agentworkforce login was hitting POST /api/v1/workspaces/{id}/tokens/workspace
(and two fallback paths) to mint a workspace-scoped token. Cloud does not
implement any of those routes -- all three 404, blocking login for every
user who already has a valid agent-relay cloud login.
Cloud's resolveRequestAuth already accepts the user's accessToken from
~/.agent-relay/cloud-auth.json as Authorization: Bearer. The mint step
exists for CI/service-account use, not interactive CLI use, but currently
blocks BOTH because cloud has not shipped the route.
This PR:
- Drops issueWorkspaceToken from runLogin.
- Persists a lightweight ~/.agentworkforce/active.json pointer
(workspaceId + workspaceSlug + cloudUrl) at login time.
- resolveWorkspaceToken reads active.json + @agent-relay/cloud's shared
auth and returns auth.accessToken as the Bearer. Refreshes the
accessToken via refreshStoredAuth when expired.
- Preserves the WORKFORCE_WORKSPACE_TOKEN env fallback (CI) and the
legacy keychain-stored workspace token path (back-compat for users
mid-upgrade).
- runLogout always clears active.json; --cloud-auth/--all still clears
the shared agent-relay auth as before.
Layered on top of workforce#112 (--workspace short-circuit) so login
works end-to-end with just a known workspace id.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThis PR refactors workspace authentication to eliminate workspace-token minting in favor of active-workspace-pointer persistence, and implements tiered credential resolution combining shared cloud auth with active workspace selection. The changes span new contracts, updated CLI commands, comprehensive test coverage, and expanded public API exports. ChangesWorkspace Authentication via Active Pointer and Tiered Token Resolution
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
… errors (#118) * fix(cli): orchestrator + list/destroy read active.json + cloud-auth.json The deploy orchestrator was still using the legacy `envWorkspaceAuth()` default, which only consults `WORKFORCE_WORKSPACE_TOKEN` env + a long-dead keychain. A user who freshly ran `agentworkforce login` (which writes the shared @agent-relay/cloud accessToken + an active.json pointer) would hit `no workspace resolved` because that flow is invisible to the env-only resolver. PR #113 fixed the cloud launcher and list/destroy commands but missed this orchestrator entry point. The list and destroy CLI commands also defaulted to `https://agentrelay.com` (missing the `/cloud` basePath), so every API call landed on the marketing site's Next.js 404 page — and the full HTML response body was dumped verbatim into the CLI error message. Comprehensive fix: * Add `resolveCloudUrl()` as the single source of truth for cloud URL resolution (flag → env → active.json → canonical default). All three CLI commands and the orchestrator now route through it. The canonical default is now applied via `canonicalizeCloudUrl`, which also remaps the bare apex `agentrelay.com` → `agentrelay.com/cloud` to prevent the marketing-site fallthrough from ever happening again. * Add `formatHttpErrorBody()` — detects HTML response bodies and replaces them with a one-line hint, truncates long non-HTML bodies. list and destroy both use it. * Swap the orchestrator's auth default from `envWorkspaceAuth()` to `resolveWorkspaceToken()`, which respects the same env vars (Tier 1 for CI) but additionally falls through to the shared cloud-auth + active.json pointer. * Add `WORKFORCE_DISABLE_SHARED_AUTH` opt-out for hermetic tests and power users who want strictly env-only operation. Tests: 17 new (cloud-url, error-format, deploy, destroy) covering URL resolution precedence, apex canonicalization, HTML body sanitization, the deploy env-Tier 1 path, deploy noPrompt error message, destroy active.json fallback, and the test isolation hook. Smoke verified against the local build in proactive-agents: `agentworkforce deploy ... --no-prompt` now resolves the workspace from active.json, finds both notion and github already connected, and stages the bundle (failing later on harness creds, which is M3 scope). `agentworkforce deployments list` returns clean results instead of a 404 HTML wall. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(cli): address pr review comments --------- Co-authored-by: Ricky Schema Cascade <ricky@agent-relay.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Why this is blocking
agentworkforce loginruns three steps:ensureAuthenticated()from@agent-relay/cloud— reuses the shared auth at~/.agent-relay/cloud-auth.json(works today)listWorkspacesForLogin()— hitsGET /api/v1/workspaces, returns 403 for the customer's accessToken (workforce#112 mitigates with--workspaceshort-circuit)issueWorkspaceToken()— probes three cloud routes to mint a workspace-scoped token — all three are 404 because cloud does not implement any of themReal-world repro against prod today:
So even after workforce#112, step 3 still fails because the route literally doesn't exist on cloud.
Why we don't need a separate workspace-scoped token
The user is already authenticated via
agent-relay cloud login. Cloud's existingresolveRequestAuthalready accepts the user's accessToken asAuthorization: Bearer. The "workspace-scoped token" mint step was designed for CI/headless use, but for an interactive user it's gratuitous — and currently breaks the entire login flow.What this PR does
Make
agentworkforce loginreuse~/.agent-relay/cloud-auth.jsonend-to-end:issueWorkspaceTokenfromrunLogin.~/.agentworkforce/active.jsonpointer (workspace+workspaceSlug+workspaceId+cloudUrl+setAt). Non-secret metadata. 0600 permissions.resolveWorkspaceTokenreadsactive.json+ the shared@agent-relay/cloudauth and returnsauth.accessTokenas the Bearer. Refreshes the accessToken viarefreshStoredAuthwhen it's expired.WORKFORCE_WORKSPACE_TOKENenv fallback (CI path, unchanged).runLogoutalways clearsactive.json.--cloud-auth/--allstill clears the shared agent-relay login as before (per workforce#111).Resolution precedence in
resolveWorkspaceTokenWORKFORCE_WORKSPACE_TOKENenv (CI / headless)@agent-relay/cloudaccessToken +active.jsonworkspace pointeragentworkforce login" errorLayering
Based on
origin/fix/cli-login-workspace-shortcircuit(workforce#112), so login works end-to-end with just a known workspace id once both ship. The PRs are complementary — #112 sidesteps the 403 on workspaces-list, this one sidesteps the 404 on token-mint.Verification
Test plan
~/.agent-relay/cloud-auth.json:agentworkforce login --workspace <ws-id>exits 0 and writes~/.agentworkforce/active.json.agentworkforce deployreuses the active pointer + shared accessToken without prompting again.agentworkforce logoutclearsactive.jsonbut leaves~/.agent-relay/cloud-auth.jsonintact.agentworkforce logout --cloud-authclears both.WORKFORCE_WORKSPACE_TOKENset continue to work (env tier still takes precedence over the shared-auth tier).🤖 Generated with Claude Code