Skip to content

feat(channels): agents tui logs in to Cloudflare Access by itself - #2462

Merged
cjol merged 2 commits into
mainfrom
channels/tui-access
Oct 2, 2026
Merged

cjol merged 2 commits into
mainfrom
channels/tui-access

Conversation

@cjol

@cjol cjol commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

This PR makes agents tui handle a Web Channel URL behind Cloudflare Access, so connecting to a deployed agent needs no token plumbing.

Why

  • Agents deployed to a team account are usually behind Access. Today the TUI connects only with a service token in the environment, or a cf-access-token header pasted by hand from cloudflared access token.
  • Developers already have cloudflared for this. The TUI can ask it for the token, the way Wrangler and the Vite plugin handle Access for remote bindings.

Public API Surface

No new exports. agents tui <url> now:

  • detects Access from the redirect a plain request to the URL's origin gets
  • runs cloudflared access token -app=<origin>, and cloudflared access login <origin> first when there is no token yet
  • sends the token as cf-access-token on the WebSocket upgrade

Service-token variables, --header cf-access-token=… and cookies still take precedence, and URLs not behind Access are untouched.

Code Changes

  • web/tui/access.ts holds the detection and the cloudflared calls, with injectable fetch and process runners. If cloudflared is missing, it explains how to pass the header instead.
  • web/tui/main.ts adds the headers before starting the TUI. --help describes the Access behaviour.
  • tui-access.test.ts covers the cases with stubbed redirects and commands.
  • Patch changeset for agents.

Devin Review

This PR makes `agents tui` handle a Web Channel URL behind Cloudflare Access, so connecting to a deployed agent needs no token plumbing.

## Why

- Agents deployed to a team account are usually behind Access. Today the TUI connects only with a service token in the environment, or a `cf-access-token` header pasted by hand from `cloudflared access token`.
- Developers already have `cloudflared` for this. The TUI can ask it for the token, the way Wrangler and the Vite plugin handle Access for remote bindings.

## Public API Surface

No new exports. `agents tui <url>` now:

- detects Access from the redirect a plain request to the URL's origin gets
- runs `cloudflared access token -app=<origin>`, and `cloudflared access login <origin>` first when there is no token yet
- sends the token as `cf-access-token` on the WebSocket upgrade

Service-token variables, `--header cf-access-token=…` and cookies still take precedence, and URLs not behind Access are untouched.

## Code Changes

- `web/tui/access.ts` holds the detection and the `cloudflared` calls, with injectable fetch and process runners. If `cloudflared` is missing, it explains how to pass the header instead.
- `web/tui/main.ts` adds the headers before starting the TUI. `--help` describes the Access behaviour.
- `tui-access.test.ts` covers the cases with stubbed redirects and commands.
- Patch changeset for `agents`.
@changeset-bot

changeset-bot Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0039286

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
agents Patch
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Note

Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.

Devin Review found 3 potential issues.

Devin Review

Comment thread packages/agents/src/experimental/channels/web/tui/access.ts Outdated
Comment on lines +21 to +22
args = { ...args, headers: { ...args.headers, ...access } };
} catch (error) {

@devin-ai-integration devin-ai-integration Bot Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 Expired Access tokens strand live sessions

When an Access token expires, sendHeadersOnUpgrade keeps sending it on reconnects. WebChannelClient retries with the same headers, leaving the TUI disconnected until restart.

Learn more

The TUI resolves an Access token once, then sendHeadersOnUpgrade captures that token in the WebSocket wrapper. The client's reconnect loop reuses the wrapper without refreshing its headers. When Access expires the token, all future upgrade attempts use an expired token until the process exits.

Example: A TUI starts at 09:00 with a token expiring at 10:00. If the socket drops at 10:05, every automatic retry uses the 09:00 token instead of obtaining a new one.

Recommended fix: Refresh cloudflared credentials before retries after an Access rejection or before reconnecting when the JWT has expired. Make the WebSocket upgrade wrapper read updated headers on each connection and avoid restarting interactive login for unrelated connection failures.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread packages/agents/src/experimental/channels/web/tui/access.ts Outdated
@agent-think

agent-think Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

✅ agents import sizes: no significant changes (68bd6a44 → 00392869, workflow run)

…ccess's cookie

Access can protect a path rather than a whole host, so the TUI now probes
and logs in to the address it opens. A cookie header skips the lookup only
when it carries CF_Authorization.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

Devin Review

Comment on lines +41 to +42
const app = url.toString();
if (!(await behindAccess(app, deps))) return {};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 Access scope differs from the description

The PR description specifies origin-based detection and login, while cloudflared receives the channel path. Confirm path-scoped application matching and update the description.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@cjol
cjol merged commit 2f3176b into main Oct 2, 2026
17 of 18 checks passed
@cjol
cjol deleted the channels/tui-access branch October 2, 2026 21:15
@github-actions github-actions Bot mentioned this pull request Oct 2, 2026
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