@viraatdas/rudder 3.9.13 → 4.0.0-alpha.2
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 +49 -27
- package/dist/cloud.js +71 -0
- package/dist/cloud.js.map +1 -1
- package/dist/main.js +6 -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/tailscale-preview.d.ts +23 -0
- package/dist/tailscale-preview.js +99 -0
- package/dist/tailscale-preview.js.map +1 -0
- package/dist/task-routing.d.ts +19 -0
- package/dist/task-routing.js +177 -0
- package/dist/task-routing.js.map +1 -0
- package/dist/version-check.js +51 -9
- package/dist/version-check.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,11 +12,18 @@
|
|
|
12
12
|
[](https://rudder.exla.ai)
|
|
13
13
|
|
|
14
14
|
Rudder is a TUI for managing coding agents. Run Claude Code, Codex and opencode
|
|
15
|
-
side by side in one native dashboard
|
|
16
|
-
jj
|
|
15
|
+
side by side in one native dashboard. Implementation workers get isolated
|
|
16
|
+
jj workspaces. See which agent needs you,
|
|
17
17
|
approve prompts, steer any agent mid-turn, and merge the results back with one
|
|
18
|
-
key.
|
|
19
|
-
`/
|
|
18
|
+
key. Type a goal and Rudder chooses discussion, one worker, or a proposed
|
|
19
|
+
multi-agent plan. `/main` explicitly selects the main checkout.
|
|
20
|
+
|
|
21
|
+
The [v4 architecture and rollout](https://github.com/exla-ai/rudder/blob/main/PLAN.md) covers the distributed workspace,
|
|
22
|
+
build-cache and persistent-engine rewrite. Early milestones do not imply that
|
|
23
|
+
the complete storage/engine migration has shipped.
|
|
24
|
+
|
|
25
|
+
This branch contains v4 alpha foundations. Install the prerelease with
|
|
26
|
+
`npm install -g @exla/rudder@next`; the stable package remains on `latest`.
|
|
20
27
|
|
|
21
28
|
> **AI agents and contributors:** this README is for using Rudder. If you are
|
|
22
29
|
> an AI agent (or a human) working on the Rudder codebase itself, read
|
|
@@ -76,12 +83,19 @@ rudder
|
|
|
76
83
|
```
|
|
77
84
|
|
|
78
85
|
With no arguments, `rudder` opens the dashboard. Type a task in the bottom input
|
|
79
|
-
and press `Enter
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
and press `Enter`. A narrow implementation starts one isolated worker; a broad
|
|
87
|
+
architectural implementation proposes a plan for approval. Questions and
|
|
88
|
+
plan-only requests open one discussion session without implementation workers.
|
|
89
|
+
An isolated worker lands in Review and merges back with `m`. Use `/main <task>`
|
|
90
|
+
for an agent working directly in the checkout, including release work.
|
|
91
|
+
`/swarm [n] <task>` remains available for competing experiments.
|
|
92
|
+
|
|
93
|
+
Routing works locally without an API key. To opt into bounded model-assisted
|
|
94
|
+
routing, set `RUDDER_TASK_ROUTING=decisions` and `OPENAI_API_KEY`; a failed or
|
|
95
|
+
slow classifier falls back to local rules. The classifier chooses a workflow;
|
|
96
|
+
your selected agent still writes the plan. `/plan` remains a compatibility
|
|
97
|
+
shortcut. Standalone `/gam` and `/nudge` have been retired; send a follow-up to
|
|
98
|
+
the agent's pane and resume a stopped agent explicitly.
|
|
85
99
|
|
|
86
100
|
If a task needs shared local context like API tokens, private URLs, account ids,
|
|
87
101
|
or environment values, save it with `/share <text>` in the task input. Rudder
|
|
@@ -121,7 +135,7 @@ to `~/.rudder/config.json`.
|
|
|
121
135
|
│ task list │ live Claude Code or Codex terminal │
|
|
122
136
|
│ status/model │ DAG-over-orchestrator, scrollback, review │
|
|
123
137
|
├───────────────┴────────────────────────────────────────────┤
|
|
124
|
-
│ task input:
|
|
138
|
+
│ task input: discuss, work, or propose a plan automatically │
|
|
125
139
|
└──────────────────────────────────────────────────────────────┘
|
|
126
140
|
```
|
|
127
141
|
|
|
@@ -179,8 +193,8 @@ your browser. The board is a second control surface, not just a monitor: open a
|
|
|
179
193
|
task to send an update while it runs, or request changes from Review to resume
|
|
180
194
|
the same agent in the same workspace. The task's update thread records your
|
|
181
195
|
directions alongside the latest worker activity. `+ Task` follows the same
|
|
182
|
-
|
|
183
|
-
|
|
196
|
+
task routing as the native dashboard when that dashboard owns the project.
|
|
197
|
+
The standalone board retains its detached-worker workflow.
|
|
184
198
|
|
|
185
199
|
The board also works on its own with `rudder board`: in that mode updates
|
|
186
200
|
redirect or resume the detached worker directly and move revised work back
|
|
@@ -309,9 +323,7 @@ running, Rudder also exposes matching project skills; the orchestrator can write
|
|
|
309
323
|
| --- | --- |
|
|
310
324
|
| `/model` | Pick provider, then model, then effort |
|
|
311
325
|
| `/fast` | Fast mode for new agents: flagship model at low effort (Claude opus / Codex gpt-6.1-sol); `/model` switches back |
|
|
312
|
-
| `/plan <text>` | Start the orchestrator / DAG planner |
|
|
313
326
|
| `/swarm [n] <task>` | Run `n` competing experiments at one goal (leave `n` out and the swarm decides). A conversational planner proposes the experiment list; talk to it to change the list, then press `Enter` to launch. Every experiment runs on the current model in its own workspace, at most 18 at a time (`swarm.maxParallel`), and none merges on its own |
|
|
314
|
-
| `/gam [main] [<model>] <task>` | Generator + adversarial reviewer pair: split panes, the reviewer questions every round, steers the generator mid-turn, and can stop the run; only the generator edits. `^W t` shows the dialogue between them |
|
|
315
327
|
| `/share <text>` | Save gitignored shared context for all agents in `RUDDER_SHARED.md` |
|
|
316
328
|
| `/main [task]` or `/m` | Start an agent in the main checkout instead of its own workspace |
|
|
317
329
|
| `/review-all` | Combine completed workspaces and start a Codex review-all agent |
|
|
@@ -360,17 +372,17 @@ disable both capabilities so planning cannot cause external side effects.
|
|
|
360
372
|
|
|
361
373
|
## One-Off and Planning
|
|
362
374
|
|
|
363
|
-
Type a task in the bottom input
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
375
|
+
Type a task in the bottom input. Rudder chooses discussion, one isolated worker
|
|
376
|
+
or a proposed plan from the request. A standalone implementation worker touches
|
|
377
|
+
its workspace, lands in Review and merges back with `m` or `/merge-all`.
|
|
378
|
+
A slash command Rudder does not
|
|
367
379
|
know is reported as unknown instead of being sent to an agent as a task.
|
|
368
380
|
|
|
369
381
|
Use `/main <task>` for an agent in the main checkout itself, for work that has to
|
|
370
382
|
happen there (release, push, deploy). Several may run there at once; they edit
|
|
371
383
|
the same tree, so they can overwrite each other, and Rudder says so in the
|
|
372
|
-
activity log rather than stopping you.
|
|
373
|
-
|
|
384
|
+
activity log rather than stopping you. Larger implementation goals can select
|
|
385
|
+
the DAG path automatically: the read-only planner decomposes the task, then separate
|
|
374
386
|
isolated workers implement its nodes. To change a running plan, talk to that
|
|
375
387
|
orchestrator's own pane.
|
|
376
388
|
|
|
@@ -448,7 +460,7 @@ session, main checkout) instead of forking it into a worker.
|
|
|
448
460
|
|
|
449
461
|
## Worktrees and merging
|
|
450
462
|
|
|
451
|
-
**Who merges what:**
|
|
463
|
+
**Who merges what:** approved DAG nodes integrate into your checkout
|
|
452
464
|
**automatically** as soon as they finish — no per-node confirmation — so
|
|
453
465
|
dependent nodes unblock and the chain keeps flowing (their row shows
|
|
454
466
|
`done · auto-merging`, then `merged locally`; the final gate runs the repo's
|
|
@@ -577,11 +589,21 @@ reach an agent (a menu is open in its pane) says so instead of vanishing. Claude
|
|
|
577
589
|
panes in the cloud also run with Remote Control, so they appear in the Claude
|
|
578
590
|
app too.
|
|
579
591
|
|
|
580
|
-
**Previews.**
|
|
581
|
-
(`rudder cloud secrets set TS_AUTHKEY`)
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
592
|
+
**Previews.** Enroll the home machine with a Tailscale auth key in the vault
|
|
593
|
+
(`rudder cloud secrets set TS_AUTHKEY`). It reuses its persisted login on later
|
|
594
|
+
boots. Connect your laptop to the same tailnet and run `rudder cloud preview`
|
|
595
|
+
from the project to open its ready dev server, or `rudder cloud preview --list`
|
|
596
|
+
to inspect the links. Pass a port when several servers are running.
|
|
597
|
+
|
|
598
|
+
Ready project previews also appear in the app. Rudder checks the listener and
|
|
599
|
+
tailnet state before advertising it, removes stale links and prefers private
|
|
600
|
+
HTTPS Serve. If tailnet certificates are unavailable, the link identifies a
|
|
601
|
+
private HTTP fallback. This does not create a public Funnel. Discovery polls
|
|
602
|
+
every five seconds; first-time enrollment/certificate setup can take longer.
|
|
603
|
+
|
|
604
|
+
Home project tasks in the web/phone app use automatic planning. Legacy task
|
|
605
|
+
sails retain direct execution in this initial v4 track; they refuse discussion
|
|
606
|
+
or decomposition requests and point you to the home dashboard instead.
|
|
585
607
|
|
|
586
608
|
Inside the dashboard, `/login` starts browser auth and `/cloud` toggles cloud
|
|
587
609
|
mode: while it is on, tasks you type start as workers in Rudder Cloud and keep
|
package/dist/cloud.js
CHANGED
|
@@ -14,6 +14,7 @@ import { runLaptopOnboarding } from "./onboarding.js";
|
|
|
14
14
|
import { renderQrForTerminal } from "./qr.js";
|
|
15
15
|
import { describeSync, exportWork, importWork, syncCheckout } from "./checkout-sync.js";
|
|
16
16
|
import { pullFromHome } from "./cloud-pull.js";
|
|
17
|
+
import { cloudPreviews, localTailnetStatus, previewAccess, previewReachable } from "./tailscale-preview.js";
|
|
17
18
|
import { ensureDir, commandExists, expandHome, isTty, newRunId, nowIso, pathExists, promptConfirm, promptText, promptSelect, promptSecret, readJson, runCommand, shortenHome, shellQuote, writeJson, } from "./util.js";
|
|
18
19
|
const DEFAULT_LOGIN_INTERVAL_MS = 2000;
|
|
19
20
|
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
|
|
@@ -136,6 +137,9 @@ export async function runCloudCommand(command, args, options = {}) {
|
|
|
136
137
|
case "home":
|
|
137
138
|
await homeCommand(rest, options);
|
|
138
139
|
return;
|
|
140
|
+
case "preview":
|
|
141
|
+
await cloudPreviewCommand(rest, options);
|
|
142
|
+
return;
|
|
139
143
|
case "doctor":
|
|
140
144
|
await cloudDoctor(options);
|
|
141
145
|
return;
|
|
@@ -3734,6 +3738,8 @@ Usage:
|
|
|
3734
3738
|
holds every repo) and attach to its dashboard. --snapshot uploads this
|
|
3735
3739
|
folder to its own machine instead.
|
|
3736
3740
|
rudder cloud home [status|start|stop|open <owner/repo>]
|
|
3741
|
+
rudder cloud preview [port] [--list] [--json]
|
|
3742
|
+
open this repo's private Tailscale preview; list ready URLs with --list
|
|
3737
3743
|
rudder cloud move [runId...] [--yes]
|
|
3738
3744
|
move this repo's unfinished agents (or just the named ones) to the
|
|
3739
3745
|
home machine: their jj changes and conversations go along, nothing is
|
|
@@ -4700,6 +4706,71 @@ async function homeAgentsCommand(client, sub, args, options) {
|
|
|
4700
4706
|
? "The home machine is asleep; it deletes them as soon as it is back."
|
|
4701
4707
|
: "Deleted on the home machine.");
|
|
4702
4708
|
}
|
|
4709
|
+
async function cloudPreviewCommand(args, options) {
|
|
4710
|
+
const listOnly = options.json || args.includes("--list");
|
|
4711
|
+
const positional = args.filter((arg) => arg !== "--list");
|
|
4712
|
+
if (positional.length > 1 || (positional[0] && !/^\d{2,5}$/.test(positional[0]))) {
|
|
4713
|
+
throw new Error("Usage: rudder cloud preview [port] [--list] [--json]");
|
|
4714
|
+
}
|
|
4715
|
+
const requestedPort = positional[0] ? Number(positional[0]) : null;
|
|
4716
|
+
if (requestedPort !== null && (requestedPort < 1024 || requestedPort > 65535)) {
|
|
4717
|
+
throw new Error("Preview port must be between 1024 and 65535.");
|
|
4718
|
+
}
|
|
4719
|
+
const root = findRepoRoot();
|
|
4720
|
+
const repo = await githubSlugFromOrigin(root).catch(() => null);
|
|
4721
|
+
const project = repo ? { repo } : { name: path.basename(root) };
|
|
4722
|
+
const client = await cloudClient({ requireToken: true });
|
|
4723
|
+
let state = await client.request("/api/rudder/home", { method: "GET" });
|
|
4724
|
+
if (!listOnly && state.home && (!state.home.connected || state.home.stateLive === false)) {
|
|
4725
|
+
process.stderr.write("Waking the cloud machine for its private preview...\n");
|
|
4726
|
+
await client.request("/api/rudder/home/start", { method: "POST", body: {} });
|
|
4727
|
+
const deadline = Date.now() + 90_000;
|
|
4728
|
+
while ((!state.home?.connected || state.home.stateLive === false) && Date.now() < deadline) {
|
|
4729
|
+
await sleep(1500);
|
|
4730
|
+
state = await client.request("/api/rudder/home", { method: "GET" });
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
const ready = () => cloudPreviews(state, project).filter((entry) => requestedPort === null || entry.port === requestedPort);
|
|
4734
|
+
let previews = ready();
|
|
4735
|
+
if (!listOnly && !previews.length && state.home?.connected) {
|
|
4736
|
+
const deadline = Date.now() + 20_000;
|
|
4737
|
+
process.stderr.write("Waiting for this project's private preview...\n");
|
|
4738
|
+
while (!previews.length && state.home?.connected && Date.now() < deadline) {
|
|
4739
|
+
const tailnet = state.home.tailnet;
|
|
4740
|
+
if (tailnet && typeof tailnet === "object" && !Array.isArray(tailnet) && tailnet.error)
|
|
4741
|
+
break;
|
|
4742
|
+
await sleep(1000);
|
|
4743
|
+
state = await client.request("/api/rudder/home", { method: "GET" });
|
|
4744
|
+
previews = ready();
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
if (options.json) {
|
|
4748
|
+
printJson({ previews, connected: state.home?.connected === true, access: "tailnet", requiresTailscale: true });
|
|
4749
|
+
return;
|
|
4750
|
+
}
|
|
4751
|
+
if (!previews.length) {
|
|
4752
|
+
const tailnet = state.home?.tailnet;
|
|
4753
|
+
const detail = tailnet && typeof tailnet === "object" && !Array.isArray(tailnet) && typeof tailnet.error === "string"
|
|
4754
|
+
? ` ${tailnet.error}.` : "";
|
|
4755
|
+
throw new Error(`No ready private preview${requestedPort ? ` on port ${requestedPort}` : ""} for ${repo || project.name}.${detail} Start a dev server in that project's cloud workspace, then retry.`);
|
|
4756
|
+
}
|
|
4757
|
+
if (listOnly || previews.length > 1) {
|
|
4758
|
+
for (const preview of previews)
|
|
4759
|
+
console.log(`:${preview.port} ${preview.label} ${preview.url}`);
|
|
4760
|
+
if (!listOnly)
|
|
4761
|
+
console.log("Open one with: rudder cloud preview <port>");
|
|
4762
|
+
return;
|
|
4763
|
+
}
|
|
4764
|
+
const preview = previews[0];
|
|
4765
|
+
const access = previewAccess(await localTailnetStatus(), new URL(preview.url).hostname);
|
|
4766
|
+
if (!access.ok)
|
|
4767
|
+
throw new Error(`${access.error} Preview: ${preview.url}`);
|
|
4768
|
+
if (!(await previewReachable(preview.url))) {
|
|
4769
|
+
throw new Error(`The preview is not reachable from this computer. Check the dev server and your existing Tailscale access rules. Preview: ${preview.url}`);
|
|
4770
|
+
}
|
|
4771
|
+
console.log(preview.url);
|
|
4772
|
+
openBrowser(preview.url);
|
|
4773
|
+
}
|
|
4703
4774
|
async function homeCommand(args, options) {
|
|
4704
4775
|
const client = await cloudClient({ requireToken: true });
|
|
4705
4776
|
const sub = args[0] ?? "status";
|