Skip to content

fix(cli): reuse @agent-relay/cloud auth as Bearer; drop workspace-token mint#113

Merged
khaliqgant merged 2 commits into
mainfrom
fix/cli-reuse-shared-auth-no-token-mint
May 13, 2026
Merged

fix(cli): reuse @agent-relay/cloud auth as Bearer; drop workspace-token mint#113
khaliqgant merged 2 commits into
mainfrom
fix/cli-reuse-shared-auth-no-token-mint

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Why this is blocking

agentworkforce login runs three steps:

  1. ensureAuthenticated() from @agent-relay/cloud — reuses the shared auth at ~/.agent-relay/cloud-auth.json (works today)
  2. listWorkspacesForLogin() — hits GET /api/v1/workspaces, returns 403 for the customer's accessToken (workforce#112 mitigates with --workspace short-circuit)
  3. issueWorkspaceToken() — probes three cloud routes to mint a workspace-scoped token — all three are 404 because cloud does not implement any of them

Real-world repro against prod today:

$ ./scripts/mint-workforce-token.sh
=== POST tokens/workspace === HTTP 404 (Next.js not-found)
=== POST workspace-token  === HTTP 404
=== POST token            === HTTP 404

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 existing resolveRequestAuth already accepts the user's accessToken as Authorization: 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 login reuse ~/.agent-relay/cloud-auth.json end-to-end:

  • Drops issueWorkspaceToken from runLogin.
  • Persists a lightweight ~/.agentworkforce/active.json pointer (workspace + workspaceSlug + workspaceId + cloudUrl + setAt). Non-secret metadata. 0600 permissions.
  • resolveWorkspaceToken reads active.json + the shared @agent-relay/cloud auth and returns auth.accessToken as the Bearer. Refreshes the accessToken via refreshStoredAuth when it's expired.
  • Preserves WORKFORCE_WORKSPACE_TOKEN env fallback (CI path, unchanged).
  • Preserves 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 login as before (per workforce#111).

Resolution precedence in resolveWorkspaceToken

  1. WORKFORCE_WORKSPACE_TOKEN env (CI / headless)
  2. NEW: shared @agent-relay/cloud accessToken + active.json workspace pointer
  3. Legacy keychain workspace token (back-compat)
  4. Throws a clear "run agentworkforce login" error

Layering

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

corepack pnpm -r build                     # clean
corepack pnpm -F @agentworkforce/cli typecheck     # clean
corepack pnpm -F @agentworkforce/cli test          # 192/192
corepack pnpm -F @agentworkforce/deploy typecheck  # clean
corepack pnpm -F @agentworkforce/deploy test       # 56/56

Test plan

  • On a machine with a valid ~/.agent-relay/cloud-auth.json: agentworkforce login --workspace <ws-id> exits 0 and writes ~/.agentworkforce/active.json.
  • Subsequent agentworkforce deploy reuses the active pointer + shared accessToken without prompting again.
  • agentworkforce logout clears active.json but leaves ~/.agent-relay/cloud-auth.json intact.
  • agentworkforce logout --cloud-auth clears both.
  • CI pipelines with WORKFORCE_WORKSPACE_TOKEN set continue to work (env tier still takes precedence over the shared-auth tier).

🤖 Generated with Claude Code

Ricky Schema Cascade and others added 2 commits May 13, 2026 15:46
…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>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d380949f-1c69-4b6d-9a23-f11f7a78f0fc

📥 Commits

Reviewing files that changed from the base of the PR and between 25945b7 and ee06f8f.

📒 Files selected for processing (5)
  • packages/cli/src/deploy-command.test.ts
  • packages/cli/src/deploy-command.ts
  • packages/deploy/src/index.ts
  • packages/deploy/src/login.test.ts
  • packages/deploy/src/login.ts

📝 Walkthrough

Walkthrough

This 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.

Changes

Workspace Authentication via Active Pointer and Tiered Token Resolution

Layer / File(s) Summary
Active workspace pointer interface and storage
packages/deploy/src/login.ts, packages/deploy/src/login.test.ts
ActiveWorkspacePointer interface records selected workspace with optional slug/id and cloud URL. readActiveWorkspace, writeActiveWorkspace, and clearActiveWorkspace helpers safely read/write JSON with restrictive file permissions. Tests validate round-trip operations and storage clearing.
Tiered workspace token resolution with test infrastructure
packages/deploy/src/login.ts, packages/deploy/src/login.test.ts
resolveWorkspaceToken implements priority-based resolution: explicit WORKFORCE_WORKSPACE_ID/WORKFORCE_WORKSPACE_TOKEN env vars → refreshed shared @agent-relay/cloud auth paired with workspace from request/env/active-pointer → legacy stored token. New readSharedAuthForBearer helper handles shared auth refresh. Test harness (withActiveWorkspaceEnv) creates controlled auth scenarios. Comprehensive tests validate each tier, env precedence, workspace argument override, and fallback to legacy path when active pointer is absent.
CLI login/logout command integration
packages/cli/src/deploy-command.ts, packages/cli/src/deploy-command.test.ts
Wires clearActiveWorkspace and writeActiveWorkspace into DeployCommandDeps. runLogin selects a workspace (from --workspace flag or interactive listing) and writes the active pointer without minting tokens. runLogout always clears the active pointer first, then optionally clears shared auth and legacy tokens. Workspace listing treats HTTP 403 as a dedicated error with --workspace guidance. Help text describes pointer-based storage and clarifies no workspace-scoped token is minted. Legacy readWorkspaceToken and readWorkspaceId helpers removed. Tests verify login pointer writes for both --workspace and interactive paths, error handling for 403 and empty workspaces, and logout clearing of active pointer plus optional auth layers.
Public API re-exports
packages/deploy/src/index.ts
Expands re-export list from ./connect.js to include ActiveWorkspacePointer type and workspace/token management functions (clearActiveWorkspace, writeActiveWorkspace, readActiveWorkspace, resolveWorkspaceToken, etc.), making them directly importable from the public package interface.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

  • AgentWorkforce/workforce#111: Updates runLogout semantics for --cloud-auth/--all flags; main PR adds clearActiveWorkspace to logout and refactors the complete active-workspace-pointer flow.
  • AgentWorkforce/workforce#107: The destroy command calls resolveWorkspaceToken, which this PR substantially refactors with a new active-workspace-pointer tier and shared-auth integration.
  • AgentWorkforce/workforce#109: Parallel refactor of v1 login/logout and workspace-credential flow around shared cloud auth and active-workspace-pointer persistence; tightly coupled code-level changes to the same resolveWorkspaceToken and CLI command semantics.

Poem

🐰 A pointer takes the place of tokens minted,
Workspace selection now carefully hinted,
Tiers of auth resolve in order divine,
Shared clouds and active pointers align,
Login flows fresh, and logout's well-pinted! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: reusing shared cloud auth as Bearer and removing the workspace-token mint step that was blocking the login flow.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the problem (broken token-mint routes), the solution (reuse shared auth + active.json pointer), and the implementation approach.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cli-reuse-shared-auth-no-token-mint

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional findings.

Open in Devin Review

@khaliqgant khaliqgant merged commit 6a22f13 into main May 13, 2026
3 checks passed
@khaliqgant khaliqgant deleted the fix/cli-reuse-shared-auth-no-token-mint branch May 13, 2026 14:51
khaliqgant added a commit that referenced this pull request May 13, 2026
… 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant