@viraatdas/rudder 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -10
- package/dist/cloud.js +35 -3
- package/dist/cloud.js.map +1 -1
- package/dist/feedback.js +1 -1
- package/dist/feedback.js.map +1 -1
- package/dist/improve/index.js +2 -2
- package/dist/improve/index.js.map +1 -1
- package/dist/improve/propose.js +1 -1
- package/dist/improve/propose.js.map +1 -1
- package/dist/improve/state.js +1 -1
- package/dist/improve/state.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/native/darwin-arm64/rudder-native +0 -0
- package/dist/native/darwin-x64/rudder-native +0 -0
- package/dist/native/linux-x64/rudder-native +0 -0
- package/dist/paste-bridge.d.ts +36 -0
- package/dist/paste-bridge.js +279 -0
- package/dist/paste-bridge.js.map +1 -0
- package/dist/usage.js +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/version-check.d.ts +2 -0
- package/dist/version-check.js +34 -5
- package/dist/version-check.js.map +1 -1
- package/package.json +3 -3
- package/scripts/postinstall.mjs +1 -1
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# rudder
|
|
2
2
|
|
|
3
3
|
<p>
|
|
4
|
-
<a href="https://rudder.
|
|
5
|
-
<img src="https://rudder.
|
|
4
|
+
<a href="https://rudder.exla.ai">
|
|
5
|
+
<img src="https://rudder.exla.ai/favicon.svg" width="36" height="36" alt="Rudder logo" />
|
|
6
6
|
</a>
|
|
7
7
|
</p>
|
|
8
8
|
|
|
9
|
-
[](https://github.com/exla-ai/rudder/actions/workflows/tui.yml)
|
|
10
|
+
[](https://www.npmjs.com/package/@exla/rudder)
|
|
11
|
+
[](https://nodejs.org/)
|
|
12
|
+
[](https://rudder.exla.ai)
|
|
13
13
|
|
|
14
14
|
Rudder is a TUI for managing coding agents. Run Claude Code, Codex and opencode
|
|
15
15
|
side by side in one native dashboard: every task you type gets its own isolated
|
|
@@ -26,26 +26,30 @@ key. `/main` puts an agent in the main checkout when you want one there, and
|
|
|
26
26
|
## Install
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npm install -g @
|
|
29
|
+
npm install -g @exla/rudder@latest
|
|
30
30
|
rudder
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
After the global install, every user-facing `rudder` launch checks npm and
|
|
34
34
|
auto-updates itself before running when a newer version is available. Set
|
|
35
35
|
`RUDDER_DISABLE_AUTO_UPDATE=1` to opt out. Run without a global install using
|
|
36
|
-
`npx @
|
|
36
|
+
`npx @exla/rudder@latest`.
|
|
37
|
+
|
|
38
|
+
Rudder used to be published as `@viraatdas/rudder`. Existing installs move
|
|
39
|
+
themselves to `@exla/rudder` on their next update; nothing to do by hand.
|
|
37
40
|
|
|
38
41
|
## Requirements
|
|
39
42
|
|
|
40
43
|
- Node.js 22 or newer
|
|
41
44
|
- Git
|
|
42
45
|
- Claude Code and/or Codex installed and logged in
|
|
46
|
+
- macOS (Apple Silicon or Intel) or Linux x64; the dashboard binary for each
|
|
47
|
+
ships in the package
|
|
43
48
|
|
|
44
49
|
Rudder runs agents in isolated [jj (Jujutsu)](https://jj-vcs.github.io)
|
|
45
50
|
workspaces; your repo stays plain git. You do not need to install jj: if it is
|
|
46
51
|
not already on your PATH, Rudder sets up its own copy in `~/.rudder/bin` during
|
|
47
52
|
install (or on first run). A jj you installed yourself is always used instead.
|
|
48
|
-
- macOS, Linux, or another Unix-like terminal
|
|
49
53
|
|
|
50
54
|
Check your setup at any time:
|
|
51
55
|
|
|
@@ -145,6 +149,16 @@ between files, `r` refreshes, `Esc` or `⌥d` closes it, and `⌥h` solos it lik
|
|
|
145
149
|
any other pane. On a narrow terminal it takes the worker's place instead of
|
|
146
150
|
squeezing both. `v` opens the same panel.
|
|
147
151
|
|
|
152
|
+
### God's view
|
|
153
|
+
|
|
154
|
+
Press `⌥g` (also `^W g`) to see every running agent at once: the screen turns
|
|
155
|
+
into a grid with one tile per agent showing the bottom of its terminal, titled
|
|
156
|
+
with its id, task, model and whether it is working, needs you, or is done.
|
|
157
|
+
Arrows or `h`/`j`/`k`/`l` move the highlight, `Enter` or a click opens that
|
|
158
|
+
agent in the worker pane, and `⌥g` or `Esc` closes the grid. Agents are never
|
|
159
|
+
resized for it, so their scrollback is untouched. With more agents than fit,
|
|
160
|
+
the grid pages.
|
|
161
|
+
|
|
148
162
|
### Web board
|
|
149
163
|
|
|
150
164
|
Press `o` in the agents pane (or type `/web`) to open the live project board in
|
|
@@ -168,6 +182,9 @@ through Review before it can merge.
|
|
|
168
182
|
| `Option-1` / `Option-2` / `Option-3` | Focus the agents, worker, or task pane |
|
|
169
183
|
| `Option-[` / `Option-]` | Step to the previous / next agent, staying in the pane you are in |
|
|
170
184
|
| `Option-h` | Full screen: the focused pane takes the whole terminal; press again to bring the panes back |
|
|
185
|
+
| `Option-g` | God's view: every agent tiled on one screen; press again to close |
|
|
186
|
+
| `Option-d` | Diff panel for the selected agent |
|
|
187
|
+
| `Ctrl-V` | Paste a screenshot or copied files into an agent (works in `rudder cloud` too) |
|
|
171
188
|
| `Option-v` / `Option-b` | Previous question in the selected worker / back to its latest message (Claude Code and Codex) |
|
|
172
189
|
| `Cmd-C` | Copy the active Rudder selection |
|
|
173
190
|
| `Ctrl-C` | Quit (asks to confirm if agents are still running) |
|
|
@@ -483,6 +500,7 @@ rudder login # sign in with Google (opens straight to the accoun
|
|
|
483
500
|
rudder cloud # open this repo on your home machine and attach to it
|
|
484
501
|
rudder cloud home # home machine status: projects, agents, previews
|
|
485
502
|
rudder cloud move # move this repo's running agents to the home machine
|
|
503
|
+
rudder cloud sync [repo] # bring this checkout and the cloud's clone level (/sync)
|
|
486
504
|
rudder cloud app # the phone app link
|
|
487
505
|
rudder cloud list # list cloud workers
|
|
488
506
|
rudder cloud logs <id> # worker status
|
|
@@ -505,6 +523,26 @@ agent state live on its disk, so agents resume where they were.
|
|
|
505
523
|
`rudder cloud --snapshot` keeps the old behavior: upload this folder to a
|
|
506
524
|
machine of its own.
|
|
507
525
|
|
|
526
|
+
**Same repo on the laptop and the cloud (`/sync`).** `/sync` in the dashboard
|
|
527
|
+
(or `rudder cloud sync`) brings this checkout and the cloud machine's clone of
|
|
528
|
+
it up to the newest default branch, and carries the work GitHub does not have
|
|
529
|
+
yet, uncommitted edits included, from one side to the other directly. Nothing is
|
|
530
|
+
pushed to your repo, and gitignored files such as `.env` never travel. The side
|
|
531
|
+
receiving work only takes it when nothing of its own would be lost; when both
|
|
532
|
+
sides changed, both versions are kept and the other one waits as the jj bookmark
|
|
533
|
+
`rudder-sync/cloud` (or `rudder-sync/laptop`). `rudder cloud sync <repo>` with a
|
|
534
|
+
name clones a repo that so far exists only on the cloud into `~/code/<repo>`.
|
|
535
|
+
`/sync all` covers every repo, and `/sync move` hands this laptop's running
|
|
536
|
+
agents to the cloud.
|
|
537
|
+
|
|
538
|
+
**Screenshots and files.** In `rudder cloud`, `Ctrl-V` with a screenshot or
|
|
539
|
+
files copied in Finder, or dragging a file into the terminal, uploads it to the
|
|
540
|
+
cloud machine and gives the agent its path there: Claude Code attaches an image
|
|
541
|
+
as `[Image #1]` exactly as it does locally. Up to 25 MB per file.
|
|
542
|
+
|
|
543
|
+
**Updates.** After a release the cloud machine updates itself once its agents
|
|
544
|
+
have been idle for five minutes, so you never restart it by hand.
|
|
545
|
+
|
|
508
546
|
**Phone.** Open `rudder cloud app` on your phone and sign in with the same
|
|
509
547
|
Google account. You see every project and agent, read conversations as they
|
|
510
548
|
happen, text any agent, start tasks (`/model` picks Claude or Codex for new ones),
|
|
@@ -678,7 +716,7 @@ usage event, and a GitHub issue when `gh` is installed and authenticated. Add
|
|
|
678
716
|
## Building from source
|
|
679
717
|
|
|
680
718
|
```bash
|
|
681
|
-
git clone https://github.com/
|
|
719
|
+
git clone https://github.com/exla-ai/rudder.git
|
|
682
720
|
cd rudder
|
|
683
721
|
./setup.sh
|
|
684
722
|
```
|
package/dist/cloud.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { createHash, randomUUID } from "node:crypto";
|
|
3
3
|
import { AttachSession, containsCtrlC } from "./attach-session.js";
|
|
4
|
+
import { PasteBridge, readLocalClipboard } from "./paste-bridge.js";
|
|
4
5
|
import fsp from "node:fs/promises";
|
|
5
6
|
import os from "node:os";
|
|
6
7
|
import path from "node:path";
|
|
@@ -749,7 +750,7 @@ async function quickstart(options) {
|
|
|
749
750
|
"Rudder Cloud — copy/paste quickstart",
|
|
750
751
|
"",
|
|
751
752
|
"1. Install Rudder:",
|
|
752
|
-
" npm install -g @
|
|
753
|
+
" npm install -g @exla/rudder@latest",
|
|
753
754
|
"",
|
|
754
755
|
"2. Log in to the cloud control plane:",
|
|
755
756
|
" rudder login",
|
|
@@ -2851,6 +2852,22 @@ function raceLeave(session, promise) {
|
|
|
2851
2852
|
promise.then((value) => { off(); resolve(value); }, (error) => { off(); reject(error); });
|
|
2852
2853
|
});
|
|
2853
2854
|
}
|
|
2855
|
+
// One pasted file up to the home machine, through the phone's upload route.
|
|
2856
|
+
async function uploadPastedFile(baseUrl, token, workspaceId, file, name) {
|
|
2857
|
+
const bytes = await fsp.readFile(file);
|
|
2858
|
+
const url = `${baseUrl.replace(/\/$/, "")}/api/app/upload?workspace=${encodeURIComponent(workspaceId)}&name=${encodeURIComponent(name)}`;
|
|
2859
|
+
const response = await fetch(url, {
|
|
2860
|
+
method: "POST",
|
|
2861
|
+
headers: { authorization: `Bearer ${token}`, "content-type": "application/octet-stream" },
|
|
2862
|
+
body: bytes,
|
|
2863
|
+
signal: AbortSignal.timeout(90_000),
|
|
2864
|
+
});
|
|
2865
|
+
const body = await response.json().catch(() => ({}));
|
|
2866
|
+
if (!response.ok || typeof body.path !== "string") {
|
|
2867
|
+
throw new Error(body.error || `HTTP ${response.status}`);
|
|
2868
|
+
}
|
|
2869
|
+
return body.path;
|
|
2870
|
+
}
|
|
2854
2871
|
async function runAttach(target, options, sharedSession) {
|
|
2855
2872
|
const client = await cloudClient({ requireToken: true });
|
|
2856
2873
|
const baseUrl = client.baseUrl;
|
|
@@ -2938,6 +2955,22 @@ async function runAttach(target, options, sharedSession) {
|
|
|
2938
2955
|
}
|
|
2939
2956
|
resolve(value);
|
|
2940
2957
|
};
|
|
2958
|
+
// Files and clipboard images from this computer reach the cloud machine
|
|
2959
|
+
// as uploads (src/paste-bridge.ts); only the home's repo rows take them.
|
|
2960
|
+
const paste = new PasteBridge({
|
|
2961
|
+
send: (data) => {
|
|
2962
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
2963
|
+
socket.send(data, { binary: true });
|
|
2964
|
+
predictor?.onInput(data);
|
|
2965
|
+
}
|
|
2966
|
+
},
|
|
2967
|
+
upload: (file, name) => uploadPastedFile(baseUrl, token, target.id, file, name),
|
|
2968
|
+
readClipboard: target.kind === "workspace" && isInteractive ? readLocalClipboard : undefined,
|
|
2969
|
+
status: (text) => (text ? session.showStatus(text) : session.clearStatus()),
|
|
2970
|
+
});
|
|
2971
|
+
if (target.kind !== "workspace") {
|
|
2972
|
+
paste.disabled = true;
|
|
2973
|
+
}
|
|
2941
2974
|
let lastCtrlC = 0;
|
|
2942
2975
|
const onStdin = (chunk) => {
|
|
2943
2976
|
const buffer = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
@@ -2998,8 +3031,7 @@ async function runAttach(target, options, sharedSession) {
|
|
|
2998
3031
|
}
|
|
2999
3032
|
lastCtrlC = now;
|
|
3000
3033
|
}
|
|
3001
|
-
|
|
3002
|
-
predictor?.onInput(buffer);
|
|
3034
|
+
paste.forward(buffer);
|
|
3003
3035
|
};
|
|
3004
3036
|
const onResize = () => {
|
|
3005
3037
|
if (hasControl) {
|