@viraatdas/rudder 3.5.7 → 3.6.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.
@@ -0,0 +1,46 @@
1
+ import type { JsonValue } from "./types.js";
2
+ export type PullExport = {
3
+ head: string;
4
+ base: string | null;
5
+ title: string;
6
+ task: string;
7
+ backend: string;
8
+ model: string | null;
9
+ sessionId: string | null;
10
+ };
11
+ export type PullImport = {
12
+ path: string;
13
+ runId: string;
14
+ title: string;
15
+ resumable: boolean;
16
+ };
17
+ /**
18
+ * Home side: bundle run `runId` of the clone at `repoRoot` into `outFile` (a
19
+ * .tgz move bundle with one agent). Any status pulls, the main row included:
20
+ * its `@` is the clone's own working copy.
21
+ */
22
+ export declare function exportRunForPull(repoRoot: string, runId: string, outFile: string): Promise<PullExport>;
23
+ /**
24
+ * Laptop side: adopt a bundle made by exportRunForPull into the checkout at
25
+ * `repoRoot`. A run with the same id already here is replaced only when it is
26
+ * the row that moved to the home machine, or an earlier pull nothing runs.
27
+ */
28
+ export declare function importPulledRun(repoRoot: string, archive: string, homeDir?: string): Promise<PullImport>;
29
+ /** The slice of the cloud client a pull needs (src/cloud.ts builds it). */
30
+ export type PullClient = {
31
+ request<T>(pathOrUrl: string, init: {
32
+ method: string;
33
+ body?: JsonValue;
34
+ }): Promise<T>;
35
+ download(pathname: string): Promise<Buffer>;
36
+ };
37
+ /**
38
+ * `rudder cloud pull <runId> [--json]`, in a local clone of a repo open on the
39
+ * home machine.
40
+ */
41
+ export declare function pullFromHome(args: string[], options: {
42
+ json?: boolean;
43
+ }, deps: {
44
+ client: PullClient;
45
+ githubSlug: (root: string) => Promise<string | null>;
46
+ }): Promise<PullImport>;
@@ -0,0 +1,228 @@
1
+ // Pulling one agent from the home machine back to this computer: the reverse
2
+ // of `rudder cloud move` (src/move-home.ts), and built from the same parts.
3
+ //
4
+ // home exportRunForPull (`rudder __pull-export`, run by the supervisor):
5
+ // a move bundle holding the run's jj `@` commit (the working copy is
6
+ // snapshotted first, so uncommitted edits come along; what origin's
7
+ // default branch has stays behind), its run.json and its Claude
8
+ // transcript or Codex rollout, tarred. The supervisor PUTs it to the
9
+ // control plane as a one-shot sync blob.
10
+ // laptop pullFromHome (`rudder cloud pull <run>`): ask for it, download the
11
+ // blob, and adopt it like the home adopts a move: a jj workspace under
12
+ // .rudder-workspaces whose `@` IS the pulled commit, the conversation
13
+ // placed where `--resume` looks for it at the new cwd, and a `paused`
14
+ // run.json the dashboard lists and resumes on Enter.
15
+ //
16
+ // It is a copy, not a move: the agent keeps running on the home machine.
17
+ import fsp from "node:fs/promises";
18
+ import os from "node:os";
19
+ import path from "node:path";
20
+ import { findRepoRoot } from "./git.js";
21
+ import { currentJjCommitId } from "./jj.js";
22
+ import { adoptMovedAgents, buildMoveBundle, findCodexMainThreadForCwd, } from "./move-home.js";
23
+ import { adoptLegacyWorkspaceKey, AGENT_WORKSPACES_DIR, ensureProjectRuntimeIgnored, workspacePath } from "./state.js";
24
+ import { nowIso, pathExists, readJson, runCommand, shortHash, writeJson } from "./util.js";
25
+ const RUN_ID_RE = /^[A-Za-z0-9._-]{1,120}$/;
26
+ function validRunId(runId) {
27
+ return RUN_ID_RE.test(runId) && !runId.startsWith(".");
28
+ }
29
+ async function originDefaultCommit(repoRoot) {
30
+ const head = await runCommand("git", ["symbolic-ref", "-q", "refs/remotes/origin/HEAD"], { cwd: repoRoot, allowFailure: true });
31
+ for (const ref of [head.stdout.trim(), "refs/remotes/origin/main", "refs/remotes/origin/master"].filter(Boolean)) {
32
+ const sha = await runCommand("git", ["rev-parse", "--verify", "-q", ref], { cwd: repoRoot, allowFailure: true });
33
+ if (sha.code === 0) {
34
+ return sha.stdout.trim();
35
+ }
36
+ }
37
+ return null;
38
+ }
39
+ /**
40
+ * Home side: bundle run `runId` of the clone at `repoRoot` into `outFile` (a
41
+ * .tgz move bundle with one agent). Any status pulls, the main row included:
42
+ * its `@` is the clone's own working copy.
43
+ */
44
+ export async function exportRunForPull(repoRoot, runId, outFile) {
45
+ if (!validRunId(runId)) {
46
+ throw new Error("bad run id");
47
+ }
48
+ const record = await readJson(path.join(repoRoot, ".rudder", "runs", runId, "run.json"));
49
+ if (!record || record.id !== runId) {
50
+ throw new Error(`no agent ${runId} on the home machine`);
51
+ }
52
+ adoptLegacyWorkspaceKey(record);
53
+ const workspace = (record.workspace ?? {});
54
+ const ownWorkspace = workspace.enabled === true && typeof workspace.path === "string" && workspace.path
55
+ && (await pathExists(workspace.path)) ? workspace.path : null;
56
+ const workspaceDir = ownWorkspace ?? repoRoot;
57
+ const backend = String(record.backend ?? "claude");
58
+ const session = (record.session ?? {});
59
+ let sessionId = typeof session.nativeSessionId === "string" && session.nativeSessionId.trim() ? session.nativeSessionId.trim() : undefined;
60
+ if (!sessionId && backend === "codex") {
61
+ sessionId = (await findCodexMainThreadForCwd(workspaceDir)) ?? undefined;
62
+ }
63
+ const candidate = {
64
+ runId,
65
+ task: String(record.task ?? ""),
66
+ taskSummary: typeof record.taskSummary === "string" ? record.taskSummary : undefined,
67
+ backend,
68
+ status: String(record.status ?? ""),
69
+ workspacePath: workspaceDir,
70
+ workspaceName: ownWorkspace && typeof workspace.workspaceName === "string" ? workspace.workspaceName : undefined,
71
+ sessionId,
72
+ };
73
+ const stage = await fsp.mkdtemp(path.join(os.tmpdir(), "rudder-pull-stage-"));
74
+ try {
75
+ const manifest = await buildMoveBundle(repoRoot, [candidate], stage);
76
+ await runCommand("tar", ["-czf", outFile, "-C", stage, "."], { cwd: stage });
77
+ const agent = manifest.agents[0];
78
+ return {
79
+ head: agent.commit,
80
+ base: await originDefaultCommit(repoRoot),
81
+ title: (candidate.taskSummary || candidate.task || runId).slice(0, 200),
82
+ task: candidate.task,
83
+ backend,
84
+ model: typeof record.model === "string" ? record.model : null,
85
+ sessionId: agent.sessionId || null,
86
+ };
87
+ }
88
+ finally {
89
+ await fsp.rm(stage, { recursive: true, force: true });
90
+ }
91
+ }
92
+ /**
93
+ * Laptop side: adopt a bundle made by exportRunForPull into the checkout at
94
+ * `repoRoot`. A run with the same id already here is replaced only when it is
95
+ * the row that moved to the home machine, or an earlier pull nothing runs.
96
+ */
97
+ export async function importPulledRun(repoRoot, archive, homeDir = os.homedir()) {
98
+ const dir = await fsp.mkdtemp(path.join(os.tmpdir(), "rudder-pull-in-"));
99
+ try {
100
+ await runCommand("tar", ["-xzf", archive, "-C", dir]);
101
+ const manifest = await readJson(path.join(dir, "manifest.json"));
102
+ const agent = manifest?.agents?.[0];
103
+ if (!manifest || manifest.kind !== "rudder-move" || manifest.agents.length !== 1 || !agent || !validRunId(agent.runId)) {
104
+ throw new Error("not a Rudder pull bundle");
105
+ }
106
+ const runId = agent.runId;
107
+ const recordPath = path.join(repoRoot, ".rudder", "runs", runId, "run.json");
108
+ const existing = await readJson(recordPath);
109
+ if (existing) {
110
+ const migration = (existing.migration ?? {});
111
+ const status = String(existing.status ?? "");
112
+ const replaceable = status === "migrated" || (migration.pulledFrom === "home" && status !== "running");
113
+ if (!replaceable && migration.fromCommit !== agent.commit) {
114
+ throw new Error(`an agent ${runId} already exists in this checkout (${status || "unknown status"}); delete it first`);
115
+ }
116
+ }
117
+ // Same place under .rudder-workspaces as on the home machine, unless that
118
+ // directory is taken here (the workspace this agent left when it moved,
119
+ // or an earlier pull): then a sibling, so nothing local is overwritten.
120
+ const parts = (agent.relativeWorkspacePath ?? "").split("/");
121
+ let relative = parts.length >= 2 && parts[0] === AGENT_WORKSPACES_DIR
122
+ ? agent.relativeWorkspacePath
123
+ : path.relative(repoRoot, workspacePath(repoRoot, runId, agent.taskSummary || agent.task)).split(path.sep).join("/");
124
+ const sameCommit = existing && (existing.migration ?? {}).fromCommit === agent.commit;
125
+ if (!sameCommit && (await pathExists(path.join(repoRoot, relative)))) {
126
+ relative = `${relative}-${shortHash(`${runId}:${agent.commit}`).slice(0, 4)}`;
127
+ if (await pathExists(path.join(repoRoot, relative))) {
128
+ throw new Error(`${relative} already exists; delete it to pull this agent again`);
129
+ }
130
+ }
131
+ agent.relativeWorkspacePath = relative;
132
+ // adoptMovedAgents reads the manifest from disk.
133
+ await writeJson(path.join(dir, "manifest.json"), manifest);
134
+ // A record that moved there and back is adopted fresh, not "already adopted".
135
+ if (existing && !sameCommit) {
136
+ await fsp.rm(recordPath, { force: true });
137
+ }
138
+ await ensureProjectRuntimeIgnored(repoRoot);
139
+ const result = await adoptMovedAgents(repoRoot, dir, homeDir);
140
+ const adopted = result.adopted[0];
141
+ if (!adopted) {
142
+ if (existing && !sameCommit) {
143
+ await writeJson(recordPath, existing);
144
+ }
145
+ throw new Error(result.failed[0]?.error ?? "could not stage the agent here");
146
+ }
147
+ const record = (await readJson(recordPath)) ?? {};
148
+ const migration = (record.migration ?? {});
149
+ record.migration = { ...migration, origin: "home", pulledFrom: "home", at: nowIso(), pendingResume: false, pendingFresh: false };
150
+ delete record.movedTo;
151
+ // A main row on the home machine is an isolated worker here, and it belongs
152
+ // to no plan in this checkout.
153
+ if (record.mode === "main" || record.mode === "one-off") {
154
+ record.mode = "execute";
155
+ }
156
+ for (const key of ["nodeId", "planId", "gam", "resolverFor", "resolverRunId"]) {
157
+ delete record[key];
158
+ }
159
+ record.updatedAt = String(Date.now());
160
+ await writeJson(recordPath, record);
161
+ return {
162
+ path: adopted.workspacePath,
163
+ runId,
164
+ title: String(agent.taskSummary || agent.task || runId).slice(0, 200),
165
+ resumable: adopted.resumable,
166
+ };
167
+ }
168
+ finally {
169
+ await fsp.rm(dir, { recursive: true, force: true });
170
+ }
171
+ }
172
+ function retryable(message) {
173
+ return /\b503\b|waking|asleep|not connected|starting|did not answer|fetch failed/i.test(message);
174
+ }
175
+ /**
176
+ * `rudder cloud pull <runId> [--json]`, in a local clone of a repo open on the
177
+ * home machine.
178
+ */
179
+ export async function pullFromHome(args, options, deps) {
180
+ const runId = args.find((arg) => !arg.startsWith("-")) ?? "";
181
+ if (!validRunId(runId)) {
182
+ throw new Error("Usage: rudder cloud pull <runId> [--json] (the run id is in the app's agent menu and `rudder cloud home`)");
183
+ }
184
+ const root = findRepoRoot();
185
+ const slug = await deps.githubSlug(root);
186
+ if (!slug) {
187
+ throw new Error("Run `rudder cloud pull` inside a local clone of the GitHub repo the agent works in.");
188
+ }
189
+ const home = await deps.client.request("/api/rudder/home", { method: "GET" });
190
+ if (!(home.projects ?? []).some((project) => project.repo?.toLowerCase() === slug.toLowerCase())) {
191
+ throw new Error(`${slug} is not open on your cloud machine, so it has no agent to pull here.`);
192
+ }
193
+ if (!options.json) {
194
+ process.stderr.write("Asking the cloud machine for the agent's work (waking it if it is asleep)...\n");
195
+ }
196
+ const ask = () => deps.client.request("/api/rudder/home/pull", { method: "POST", body: { repo: slug, runId } });
197
+ let response;
198
+ try {
199
+ response = await ask();
200
+ }
201
+ catch (error) {
202
+ const message = error instanceof Error ? error.message : String(error);
203
+ if (!retryable(message)) {
204
+ throw error;
205
+ }
206
+ await new Promise((resolve) => setTimeout(resolve, 5000));
207
+ response = await ask();
208
+ }
209
+ if (!response.download || !/^\/api\/rudder\/home\/syncs\/[^/]+$/.test(response.download)) {
210
+ throw new Error("the cloud machine did not send the agent's work");
211
+ }
212
+ const scratch = await fsp.mkdtemp(path.join(os.tmpdir(), "rudder-pull-"));
213
+ try {
214
+ const archive = path.join(scratch, "pull.tgz");
215
+ await fsp.writeFile(archive, await deps.client.download(response.download));
216
+ const imported = await importPulledRun(root, archive);
217
+ // Not every jj state survives a bundle; read back what the workspace holds.
218
+ const here = await currentJjCommitId(imported.path).catch(() => "");
219
+ if (here && response.head && here !== response.head) {
220
+ process.stderr.write(`note: the workspace is at ${here.slice(0, 12)}, the cloud sent ${response.head.slice(0, 12)}\n`);
221
+ }
222
+ return { ...imported, title: response.title || imported.title };
223
+ }
224
+ finally {
225
+ await fsp.rm(scratch, { recursive: true, force: true });
226
+ }
227
+ }
228
+ //# sourceMappingURL=cloud-pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-pull.js","sourceRoot":"","sources":["../src/cloud-pull.ts"],"names":[],"mappings":"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"}
package/dist/cloud.js CHANGED
@@ -13,6 +13,7 @@ import { cloudAuthPath } from "./state.js";
13
13
  import { runLaptopOnboarding } from "./onboarding.js";
14
14
  import { renderQrForTerminal } from "./qr.js";
15
15
  import { describeSync, exportWork, importWork, syncCheckout } from "./checkout-sync.js";
16
+ import { pullFromHome } from "./cloud-pull.js";
16
17
  import { ensureDir, commandExists, expandHome, isTty, newRunId, nowIso, pathExists, promptConfirm, promptText, promptSelect, promptSecret, readJson, runCommand, shortenHome, shellQuote, writeJson, } from "./util.js";
17
18
  const DEFAULT_LOGIN_INTERVAL_MS = 2000;
18
19
  const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60 * 1000;
@@ -195,6 +196,9 @@ export async function runCloudCommand(command, args, options = {}) {
195
196
  case "move":
196
197
  await moveCommand(rest, options);
197
198
  return;
199
+ case "pull":
200
+ await pullCommand(rest, options);
201
+ return;
198
202
  case "bootstrap":
199
203
  await bootstrap(rest, options);
200
204
  return;
@@ -3656,6 +3660,10 @@ Usage:
3656
3660
  move this repo's unfinished agents (or just the named ones) to the
3657
3661
  home machine: their jj changes and conversations go along, nothing is
3658
3662
  pushed to GitHub, and they keep working with this laptop closed
3663
+ rudder cloud pull <runId> [--json]
3664
+ copy one of the home machine's agents to this computer, in a local
3665
+ clone of its repo: a jj workspace at its change (uncommitted edits
3666
+ included) and its conversation, listed paused in the dashboard
3659
3667
  rudder cloud app
3660
3668
  a one-time code that signs the phone app in to this account
3661
3669
  rudder cloud doctor
@@ -3967,6 +3975,20 @@ async function ensureVaultGithubToken(client, slug, interactive = true) {
3967
3975
  }
3968
3976
  const HOME_MOVE_POLL_MS = 1500;
3969
3977
  const HOME_MOVE_DEADLINE_MS = 15 * 60 * 1000;
3978
+ // `rudder cloud pull <runId>`: the reverse of move, for one agent (src/cloud-pull.ts).
3979
+ async function pullCommand(args, options) {
3980
+ const client = await cloudClient({ requireToken: true });
3981
+ const json = options.json || args.includes("--json");
3982
+ const pulled = await pullFromHome(args, { json }, { client, githubSlug: githubSlugFromOrigin });
3983
+ if (json) {
3984
+ printJson({ path: pulled.path, runId: pulled.runId, title: pulled.title });
3985
+ return;
3986
+ }
3987
+ console.log(`Pulled "${pulled.title}" to ${pulled.path}`);
3988
+ console.log(pulled.resumable
3989
+ ? "It is paused in the dashboard here; Enter on its row continues the conversation."
3990
+ : "It is paused in the dashboard here; its conversation did not come along, so Enter starts it fresh on its work.");
3991
+ }
3970
3992
  async function moveCommand(args, options) {
3971
3993
  const yes = options.yes || args.includes("--yes") || args.includes("-y");
3972
3994
  const runIds = args.filter((arg) => !arg.startsWith("-"));