Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
94b7913
feat: launch a coding harness against a local or upstream model with …
Siddhesh2377 Aug 21, 2026
2433759
fix: target a modern Windows so httplib can see GetAddrInfoExCancel
Siddhesh2377 Aug 21, 2026
f2a0fe2
fix: drop httplib's non-blocking resolver, which mingw cannot declare
Siddhesh2377 Aug 21, 2026
d294b32
fix: make the free-port probe build on windows, where winsock differs
Siddhesh2377 Aug 21, 2026
a986856
feat: sign in to the console from a browser and route coding through it
Siddhesh2377 Aug 22, 2026
645252d
fix: read the hostname through winsock on windows
Siddhesh2377 Aug 22, 2026
69a47d2
fix: address the review on browser sign-in and the harness
Siddhesh2377 Aug 22, 2026
b28c1fc
ci: pin the SDK to a commit so an unrelated merge cannot break a pull…
Siddhesh2377 Aug 22, 2026
536ecfb
harness: extract resolve and release so integrations can share one en…
Siddhesh2377 Aug 27, 2026
5b3de31
feat: point Claude Code, Claude Desktop and JetBrains IDEs at a model…
Siddhesh2377 Aug 27, 2026
c77a231
feat(anthropic): carry tool calls through the shim and surface in-str…
Siddhesh2377 Aug 28, 2026
8f7619d
docs(readme): document the account, editor and serve commands
Siddhesh2377 Aug 28, 2026
8f7c6ac
merge: bring the editor and Anthropic-shim work onto the 0.5 kit-cons…
Siddhesh2377 Aug 29, 2026
609e192
build: fetch cpp-httplib and nlohmann_json instead of vendoring them
Siddhesh2377 Aug 29, 2026
e4467ab
rcli: consume SDK IDL 1.2.0 and quiet backend registration logs
Siddhesh2377 Aug 30, 2026
edd8aea
docs: document console sign-in, the two binaries and the MLX bundle
Siddhesh2377 Aug 30, 2026
824b732
Harden Cloud auth and add explicit hosted OpenCode (#50)
sanchitmonga22 Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,44 @@ aliases (`run`, `pull`, `stt`). One `configure_*` wires both. See
Do not reintroduce FetchContent of the SDK, a second inference backend tree,
or a retired MetalRT / hardcoded catalog.

## Signing in to a console

`rcli login` is a device flow, the same shape `gh auth login` uses. The terminal
asks for a code, a browser the person already trusts approves it, and the
terminal collects a key. No password ever reaches the CLI.

The four endpoints it calls are **not ours to rename**: an installed binary
talks to whatever the console deploys, so a field or path change breaks every
copy in the wild. They are `POST /auth/cli/start`, `/auth/cli/poll`,
`/auth/cli/refresh`, and `GET /v1/me`. The console side has a test that reads
`src/account/console.cpp` directly and fails if the two drift.

Two secrets do different jobs. `request_code` is public and names the attempt;
`poll_secret` proves the process collecting the grant is the one that started
it. The console stores only a hash of the second.

`RCLI_CONSOLE_URL` points at the console; it defaults to `http://localhost:8080`,
which is nothing, so a local run needs it set. `RCLI_PROFILE_DIR` moves the
credential file, which is what lets several accounts share one machine.

The credential is a normal API key with the customer's credit behind it. Treat
it as one: it goes in the profile file at `0600` and nowhere else, and it is
never logged.

## Building against the SDK

`RCLI_SDK_KIT` points at a built kit, not at SDK source. `cmake/sdk-pin.cmake`
pins the IDL version and its hash; a mismatch is a hard error and the fix is to
consume a matching kit or bump the pin, **never to run protoc**.

Two binaries come out of a build. `rcli-cxx` is the CLI. `rcli` is the same
thing plus the MLX backend, and it only builds when `RCLI_SDK_SWIFT_PATH` names
an SDK checkout with the Swift tree. Ship `rcli`.

MLX resolves its Metal shaders from `mlx-swift_Cmlx.bundle` beside the
executable. Copy the binary somewhere on its own and MLX silently fails to
register, so an install puts both together and points a wrapper at them.

## Configuration and secrets

- Read environment in one place (`GlobalOptions` / `bootstrap()`).
Expand Down
39 changes: 39 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ set(RCLI_SOURCES
src/commands/cmd_voice.cpp
src/commands/cmd_rag.cpp
src/commands/cmd_bench.cpp
src/commands/cmd_editors.cpp
src/commands/cmd_harness.cpp
src/commands/cmd_account.cpp
src/account/console.cpp
src/account/credentials.cpp
src/anthropic/messages.cpp
src/anthropic/translate.cpp
src/desktop/claude_profile.cpp
src/harness/harness.cpp
src/harness/opencode.cpp
src/harness/local_models.cpp
src/ide/jetbrains_profile.cpp
src/ide/openai_proxy.cpp
src/commands/engine_options.cpp
src/commands/model_setup.cpp
src/config/cli_paths.cpp
Expand Down Expand Up @@ -79,12 +92,36 @@ endif()
target_include_directories(rcli_core PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/src"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/CLI11"
"${CMAKE_CURRENT_SOURCE_DIR}/third_party/linenoise"
)

# The Anthropic translator and the JetBrains proxy need an HTTP client and a
# JSON reader. Both used to arrive from the SDK's own source build; a kit
# consumer never configures that build, so they are fetched here.
#
# Fetched rather than vendored on purpose. Checking cpp-httplib in would drop
# thirty thousand lines of somebody else's code into this repo, where the
# security scanner reads it as ours and flags the http:// URLs an HTTP client
# is obliged to construct.
include(FetchContent)
set(HTTPLIB_REQUIRE_OPENSSL OFF CACHE BOOL "" FORCE)
set(JSON_BuildTests OFF CACHE BOOL "" FORCE)
FetchContent_Declare(cpp_httplib
GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
GIT_TAG v0.46.1
GIT_SHALLOW TRUE)
FetchContent_Declare(nlohmann_json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(cpp_httplib nlohmann_json)

find_package(Threads REQUIRED)
find_package(CURL REQUIRED)
target_link_libraries(rcli_core PUBLIC rac_commons Threads::Threads)
target_link_libraries(rcli_core PRIVATE CURL::libcurl httplib::httplib nlohmann_json::nlohmann_json)
target_compile_definitions(rcli_core PUBLIC
RCLI_VERSION="${PROJECT_VERSION}"
RCLI_PINNED_SDK_VERSION="${RCLI_PINNED_SDK_VERSION}"
Expand Down Expand Up @@ -112,6 +149,8 @@ endif()
# Include path + google=runanywhere_internal come from RunAnywhere::commons.

if(APPLE)
# SecItem, for the credential a JetBrains IDE reads its provider key from.
target_link_libraries(rcli_core PRIVATE "-framework Security" "-framework CoreFoundation")
target_link_libraries(rcli_core PUBLIC "-framework IOKit" "-framework CoreFoundation")
endif()

Expand Down
129 changes: 129 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,48 @@ rcli run qwen3

Chat, vision, speech, and embeddings — all local. Nothing leaves the device.

## Signing in

Models you have pulled run on this machine and need no account. To use a hosted
model instead, sign in to a RunAnywhere console:

```bash
rcli login # opens a browser; approve it there
rcli whoami # who you are, and what you have used this month
rcli logout
```

The terminal never asks for a password. It shows a code, you approve it in the
browser, and it collects an API key with your credit behind it. That key appears
on the console's Cloud keys page and can be revoked there at any time.

Against a console running on your own machine:

```bash
export RCLI_CONSOLE_URL=http://localhost:8002
rcli login
```

Then hand a hosted model to a coding session:

```bash
rcli opencode -m gemma-4
```

For the Open Frontier hosted path, make the choice explicit and pass any
OpenCode arguments after `--`:

```bash
rcli opencode --cloud --model <console-model-id> -- --agent build
```

`--cloud` never falls back to a local model. The existing `rcli opencode -m`
form remains available for the parent PR's local-or-upstream harness flow.

If the model is on this machine, rcli serves it locally. If it is not, the
request goes to the console you are signed in to, is checked against your
balance before it runs, and is metered.

## Install

### macOS (Apple Silicon)
Expand All @@ -23,6 +65,31 @@ or
curl -fsSL https://raw.githubusercontent.com/RunanywhereAI/RCLI/main/install.sh | sh
```

### From source

Needs a built SDK kit, not SDK source:

```bash
cmake -B build -DRCLI_SDK_KIT=<sdks>/dist/cpp-desktop-macos-arm64
export RCLI_SDK_SWIFT_PATH=<sdks> # for the MLX backend on Apple
cmake --build build -j8
```

`build/rcli` is the full binary. `build/rcli-cxx` is the same CLI without MLX,
and is what you get if `RCLI_SDK_SWIFT_PATH` is unset.

MLX loads its Metal shaders from `mlx-swift_Cmlx.bundle` next to the executable,
so install the pair together:

```bash
mkdir -p ~/.local/lib/rcli
cp -R build/mlx-swift_Cmlx.bundle build/rcli ~/.local/lib/rcli/
printf '#!/bin/sh\nexec "$HOME/.local/lib/rcli/rcli" "$@"\n' > ~/.local/bin/rcli
chmod +x ~/.local/bin/rcli
```

Copy the binary on its own and MLX will not register.

### Windows (x64)

```powershell
Expand Down Expand Up @@ -217,9 +284,71 @@ page is HTML, not a bundle.
| `rcli backends` | registered engines |
| `rcli info` | versions and paths |
| `--engine` | force mlx / llamacpp / sherpa / onnx / neurt / qhexrt |
| `rcli login` / `logout` / `whoami` | sign in to the console that serves upstream models |
| `rcli claude-code` / `claude-desktop` | open Claude against a model |
| `rcli clion` / `rustrover` | point a JetBrains IDE at a model |
| `rcli opencode` | open a coding session against a model |

`rcli --help` and `rcli <command> --help` cover the rest.

## Editors and coding agents

One command points a tool at a model and starts it. There is nothing to
configure by hand:

```bash
rcli claude-code -m qwen3-0.6b
rcli clion -m models/gemma-4-31b-it
rcli claude-desktop -m models/gemma-4-31b-it
```

The model can be one on this machine or one the console serves. Without `-m` the
tool starts the way you already have it configured, and rcli wires nothing.

| Tool | How it is wired |
| --- | --- |
| `claude-code`, `opencode` | `ANTHROPIC_BASE_URL` and `ANTHROPIC_AUTH_TOKEN` in the process |
| `claude-desktop` | a gateway profile in Claude Desktop's third party mode, covering the chat and Cowork tabs |
| `clion`, `rustrover` | AI Assistant's OpenAI-compatible provider, which works without a JetBrains AI subscription |

Two flags go with `-m`. `--serve` holds the endpoint open and prints it instead
of launching anything, which is how a tool nobody has taught rcli about gets
wired up. `--restore` puts Claude Desktop or a JetBrains IDE back the way it was
and starts nothing; a normal run already undoes its own configuration when the
app quits, so this is for the run that was interrupted before it could.

The first `rcli clion` on a machine takes a while, because it installs the AI
Assistant plugin headlessly before starting the IDE. Later runs are quick. That
endpoint sits on a fixed port rather than whatever happened to be free, because
the IDE reads the address once at startup out of a file rcli writes beforehand,
and a port that moved would leave that file naming something dead.

Claude Code and Claude Desktop speak Anthropic's Messages API, while the models
rcli serves speak OpenAI's, so a translator sits between them. It carries tool
definitions out, tool calls back, and the results of those calls out again,
which is what lets an agent on the far side run the tools it was given rather
than describe them. The JetBrains IDEs need no translator, because AI Assistant
speaks OpenAI already.

## Signing in

A model you have not downloaded can still answer, if the console serves it:

```bash
rcli login
rcli whoami
rcli run models/gemma-4-31b-it "why is the sky blue"
```

`rcli login` opens the console in a browser and waits for you to approve the
machine. Credentials land in `~/.config/rcli/credentials.json`. `rcli logout`
deletes them. `RCLI_CONSOLE_URL` points at a console other than the default and
`RCLI_PROFILE_DIR` moves where the credentials are kept.

This is separate from `rcli auth login`, which signs the device in to the
control plane with an API key. The two are being unified; see the auth work in
flight.

## Build from source

Stage a C++ desktop kit from [runanywhere-sdks](https://github.com/RunanywhereAI/runanywhere-sdks). The pin is `cmake/sdk-pin.cmake` (`RCLI_PINNED_SDK_VERSION`).
Expand Down
Loading
Loading