Skip to content

feat(cli): add nori cloud subcommand for cloud session integration#486

Open
theahura wants to merge 18 commits into
mainfrom
auto/cli-cloud-session-integration-design-20260521-191618
Open

feat(cli): add nori cloud subcommand for cloud session integration#486
theahura wants to merge 18 commits into
mainfrom
auto/cli-cloud-session-integration-design-20260521-191618

Conversation

@theahura
Copy link
Copy Markdown
Contributor

@theahura theahura commented May 21, 2026

Summary

🤖 Generated with Nori

  • Add nori cloud subcommand that runs full TUI sessions backed by cloud VMs via a broker WebSocket tunnel
  • Add broker client module with browser-based OAuth authentication, session acquire/release, and JWT credential persistence
  • Add WebSocket transport (ws_transport) bridging tokio-tungstenite to the SACP Lines interface, with SacpConnection::connect_remote() constructor
  • Add cloud disconnect detection in the relay loop that surfaces "Cloud session disconnected" errors to the user
  • Write credential files with 0600 permissions and redact auth tokens from Debug output

Test Plan

  • 552 nori-acp tests pass (29 cloud-specific: broker auth, credential persistence, JWT validation, WebSocket transport, cloud backend integration, disconnect detection)
  • 1309 nori-tui tests pass
  • 27 nori-cli tests pass (2 cloud subcommand parsing)
  • 94 E2E tests pass
  • cargo build --bin nori succeeds with zero warnings
  • just fix and just fmt clean
  • CI passes

Share Nori with your team: https://www.npmjs.com/package/nori-skillsets

theahura and others added 9 commits May 21, 2026 15:48
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Add dual-transport support to SacpConnection so it can communicate with
ACP agents over WebSocket in addition to the existing stdin/stdout path.
This is the foundation for `nori cloud` — connecting to remote agents
through a broker.

- New `ws_transport` module with `WsReadStream`, `WsSink`, and
  `connect_ws()` adapters that bridge tokio-tungstenite to the
  `sacp::Lines` transport (Text⇆String, Close→EOF, Ping/Pong→skip)
- Extract shared SACP builder setup into `establish_connection()` free
  function, used by both `spawn()` and the new `connect_remote()`
- Make `child` and `stderr_task` fields `Option<>` so remote connections
  skip process management
- 5 unit tests for ws_transport, 4 integration tests for connect_remote
- All 466 nori-acp tests pass
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
…n-integration-design-20260521-191618

# Conflicts:
#	nori-rs/acp/src/connection/mod.rs
#	nori-rs/acp/src/connection/sacp_connection.rs
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Thread CloudConnectionInfo from CLI through TUI to AcpBackend::spawn(),
enabling cloud-hosted agent sessions via WebSocket tunnel. The spawn()
method branches on cloud_connection: present → connect_remote(), absent →
local subprocess spawn. Includes CLI parsing tests and backend integration
tests with MockWsServer.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
…ect_used

The CI denies clippy::expect_used. Use ok_or_else + anyhow instead of
expect() on the broker auth_token Option.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Add BrokerClient::release_session() for POST /api/sessions/{id}/release,
called with a 5-second timeout after the TUI exits for cloud sessions.
Detect WebSocket disconnection in the relay loop and surface a clear
error message for cloud sessions instead of silently exiting. An
is_shutting_down flag prevents spurious disconnect errors during normal
user-initiated shutdown.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
…on-design-20260521-191618' into auto/cli-cloud-session-integration-design-20260521-191618
Write cloud-auth.json with 0600 permissions on Unix to prevent
other users from reading the bearer token. Redact auth_token in
Debug output for CloudConnectionInfo and CloudCredentials to avoid
accidental token leakage in logs. Add connection module docs.

New tests: binary WebSocket messages (valid/invalid UTF-8),
locally-expired JWT rejection for acquire/release, malformed
broker response handling, and file permission verification.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
@theahura theahura changed the title feat(acp): add WebSocket transport for remote ACP agents feat(cli): add nori cloud subcommand for cloud session integration May 22, 2026
theahura and others added 9 commits May 22, 2026 12:09
When broker_url is missing from both --broker-url flag and config.toml,
the CLI now prompts the user to enter it interactively. The URL is
validated and persisted to config.toml for future runs, matching the
APPLICATION_SPEC's first-run UX.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
The authenticate() method would hang indefinitely if the user never
completed the browser OAuth flow. Wrap the oneshot receiver with
tokio::time::timeout and clean up the local HTTP server on timeout
or failure via Arc<Server> + unblock().

Also updates core/docs.md data flow diagram to reflect dual transport
(subprocess or WebSocket) and refreshes CURRENT-PROGRESS.md.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
…acquire

The spec requires automatic re-triggering of the browser auth flow when a
token is expired. Previously, the cloud handler only checked has_valid_token()
at startup but did not handle BrokerError::TokenExpired from acquire_session().
Now the CLI catches TokenExpired, re-authenticates via browser OAuth, and
retries acquire_session() once. This covers edge cases like token expiry
between the validity check and the acquire call, or broker-side token
rejection (clock skew, revocation).
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
… identity

The broker uses the source field to determine which claim identity function
to use (cliClaimedBy vs webClaimedBy). Without this field, CLI sessions
were incorrectly labeled as web-originated claims.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
Keep both cloud session fields (is_cloud, is_shutting_down) and new cancel
timeout fields (prompt_task_abort, cancel_timeout_abort) from main.
🤖 Generated with [Nori](https://noriagentic.com)

Co-Authored-By: Nori <contact@tilework.tech>
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