@ricsam/r5dctl 0.0.130 → 0.0.132

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 CHANGED
@@ -136,6 +136,7 @@ r5dctl session status <session-id>
136
136
  r5dctl session prompt --worker <label> --mode agent --model max --delivery steer <session-id> "Also check the migration"
137
137
  r5dctl session prompt --worker <label> --mode agent --model max --delivery force <session-id> "Stop that and fix the build first"
138
138
  r5dctl session stop <session-id>
139
+ r5dctl session set-worker <session-id> --worker <label>
139
140
  r5dctl session process-log <run-id> --tail 200
140
141
  r5dctl session process-log <run-id> --grep "error|failed"
141
142
 
@@ -169,6 +170,8 @@ Delivery is not acknowledgment. If a merge or shared-checkout change depends on
169
170
 
170
171
  `session stop` also stops every sub-agent session the stopped session started, directly or through its own children, and `session status` prints `Child sessions: <active> active of <total>` for a session that launched sub-agents.
171
172
 
173
+ `session set-worker` rebinds a stopped session to another connected worker, exactly like the composer's worker selector: it publishes the current worker's workspace, synchronizes the target worker, persists the binding, and the session resumes there on its next prompt or resume. It is refused with `409` while the session's agent runs and with `403` for a worker credential. A running project agent moves its own session with the `switch_worker` tool instead, which needs no stop; workspace chats are rebound only while stopped. Shell runs, r5dctl shells, browser port forwards, and sub-agents started earlier stay on the worker that started them.
174
+
172
175
  `session status` reports the run lifecycle and the launch-time `Source` and `Working tree` facts only. It deliberately prints no commit hash for the worktree, not even the launch baseline: a hash would be stale by the time a caller acts on it, because commits can land between the read and the action. Find the base and the branch's commits with git in the worker checkout instead, for example `git merge-base <your-branch> <branch>`, `git log --oneline <your-branch>..<branch>`, and `git status` in the branch folder, as the `Branch state` line reminds you. While a new worktree is still provisioning, a `Provisioning` line reports where its creation stands in the worker's operation queue and since when (`Provisioning: queued behind 3 worker operations since 2026-09-03T11:41:30Z`); a worker that predates operation receipts leaves that line out.
173
176
 
174
177
  `session process-log` prints a shell run's stored stdout and stderr from the server, where r5d keeps every run's full output; the `logPath` in tool results names that server-side store, not a file on the worker. Without flags it prints the whole log, `--tail <n>` keeps the last n lines, and `--grep <pattern>` keeps the lines matching a JavaScript regular expression (combine both for the last n matches). Each stream is capped at 512 KB per call and printed under a `==> stdout <==` or `==> stderr <==` header; a one-line summary with byte totals, the selection, and any truncation goes to stderr so stdout stays clean for `grep`, `head`, and `tail`. Inside an r5d worker shell the temporary credential can only read runs on that shell's worker.
package/dist/cjs/cli.cjs CHANGED
@@ -95,6 +95,7 @@ __export(cli_exports, {
95
95
  renderSessionList: () => renderSessionList,
96
96
  renderSessionRunStart: () => renderSessionRunStart,
97
97
  renderSessionRunStatus: () => renderSessionRunStatus,
98
+ renderSessionSetWorker: () => renderSessionSetWorker,
98
99
  renderWorkerCapacity: () => renderWorkerCapacity,
99
100
  renderWorkspaceStatus: () => renderWorkspaceStatus,
100
101
  renderWorkspaceSync: () => renderWorkspaceSync,
@@ -175,6 +176,7 @@ const SESSION_RUN_HELP_TEXT = [
175
176
  " r5dctl session status <session-id>",
176
177
  ' r5dctl session prompt --worker <label> --mode <mode> --model <tier> [--delivery queue|steer|force] [--sender <name>] <session-id> "<prompt>"',
177
178
  " r5dctl session stop <session-id>",
179
+ " r5dctl session set-worker <session-id> --worker <label>",
178
180
  " r5dctl session process-log <run-id> [--tail <n>] [--grep <pattern>]",
179
181
  " r5dctl [-s <session-id>] session claim <topic> [--ttl <duration>] [--note <text>]",
180
182
  " r5dctl [-s <session-id>] session claim <topic> --status | --renew [--ttl <duration>] | --release",
@@ -197,6 +199,8 @@ const SESSION_RUN_HELP_TEXT = [
197
199
  "Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
198
200
  "process-log prints a shell run's stored stdout/stderr from the server: the whole log, its last n lines,",
199
201
  "or the lines matching a JavaScript regular expression. The log is not a file on the worker.",
202
+ "set-worker rebinds a stopped session to another connected worker: it publishes the current worker's workspace,",
203
+ "synchronizes the target, and the session resumes there. A running agent moves itself with its switch_worker tool.",
200
204
  ""
201
205
  ].join("\n");
202
206
  const SHARED_HELP_ENTRIES = [
@@ -335,6 +339,11 @@ const SHARED_HELP_ENTRIES = [
335
339
  usage: "session stop <session-id>",
336
340
  description: "Stop an active session generation."
337
341
  },
342
+ {
343
+ section: "sessions",
344
+ usage: "session set-worker <session-id> --worker <label>",
345
+ description: "Rebind a stopped session to another connected worker: publish the current worker's workspace, synchronize the target, and resume there. Refused while the agent runs; a running agent switches itself with switch_worker."
346
+ },
338
347
  {
339
348
  section: "sessions",
340
349
  usage: "[-s <session-id>] session claim <topic> [--ttl <duration>] [--note <text>] [--status|--renew|--release]",
@@ -903,6 +912,12 @@ function parseSessionRunCommand(options, args, env = process.env) {
903
912
  if (parsed.positionals.length > 1) throw new Error(`Unexpected session ${subcommand} argument: ${parsed.positionals[1]}`);
904
913
  return { kind: subcommand, sessionId };
905
914
  }
915
+ if (subcommand === "set-worker") {
916
+ const parsed = parseCommandOperands(commandArgs, /* @__PURE__ */ new Set(["--worker"]), /* @__PURE__ */ new Map(), "session set-worker");
917
+ const sessionId = requireValue(parsed.positionals[0], "Session id is required for `session set-worker`");
918
+ if (parsed.positionals.length > 1) throw new Error(`Unexpected session set-worker argument: ${parsed.positionals[1]}`);
919
+ return { kind: "set-worker", sessionId, worker: requireWorker(parsed.values) };
920
+ }
906
921
  if (subcommand === "prompt") {
907
922
  const parsed = parseCommandOperands(
908
923
  commandArgs,
@@ -2279,9 +2294,23 @@ async function dispatchSessionRunCommand(client, command) {
2279
2294
  });
2280
2295
  return { data: data2, human: renderSessionRunStatus(data2) };
2281
2296
  }
2297
+ if (command.kind === "set-worker") {
2298
+ const data2 = await client.sessions.setWorker(command.sessionId, { worker: command.worker });
2299
+ return { data: data2, human: renderSessionSetWorker(data2) };
2300
+ }
2282
2301
  const data = await client.sessions.stop(command.sessionId);
2283
2302
  return { data, human: renderSessionRunStatus(data) };
2284
2303
  }
2304
+ function renderSessionSetWorker(response) {
2305
+ const previous = typeof response.previousWorker === "string" && response.previousWorker.length > 0 ? response.previousWorker : null;
2306
+ const lines = [
2307
+ `Session: ${responseString(response, "sessionId") ?? "(unknown)"}`,
2308
+ `Worker: ${responseString(response, "worker") ?? "(unassigned)"}${previous ? ` (was ${previous})` : ""}`,
2309
+ "The session resumes on this worker; shell runs and sub-agents started earlier stay where they ran."
2310
+ ];
2311
+ return `${lines.join("\n")}
2312
+ `;
2313
+ }
2285
2314
  async function waitForSessionProvisioning(client, initial, options = {}) {
2286
2315
  const sessionId = requireValue(responseString(initial, "sessionId"), "Session start did not return a session id");
2287
2316
  const wait = options.wait ?? import_promises.setTimeout;
@@ -3880,6 +3909,7 @@ async function main(argv = process.argv.slice(2)) {
3880
3909
  renderSessionList,
3881
3910
  renderSessionRunStart,
3882
3911
  renderSessionRunStatus,
3912
+ renderSessionSetWorker,
3883
3913
  renderWorkerCapacity,
3884
3914
  renderWorkspaceStatus,
3885
3915
  renderWorkspaceSync,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.130",
3
+ "version": "0.0.132",
4
4
  "type": "commonjs"
5
5
  }
package/dist/mjs/cli.mjs CHANGED
@@ -70,6 +70,7 @@ const SESSION_RUN_HELP_TEXT = [
70
70
  " r5dctl session status <session-id>",
71
71
  ' r5dctl session prompt --worker <label> --mode <mode> --model <tier> [--delivery queue|steer|force] [--sender <name>] <session-id> "<prompt>"',
72
72
  " r5dctl session stop <session-id>",
73
+ " r5dctl session set-worker <session-id> --worker <label>",
73
74
  " r5dctl session process-log <run-id> [--tail <n>] [--grep <pattern>]",
74
75
  " r5dctl [-s <session-id>] session claim <topic> [--ttl <duration>] [--note <text>]",
75
76
  " r5dctl [-s <session-id>] session claim <topic> --status | --renew [--ttl <duration>] | --release",
@@ -92,6 +93,8 @@ const SESSION_RUN_HELP_TEXT = [
92
93
  "Use -f/--follow/--watch to attach after a start; Ctrl-C detaches without stopping the session.",
93
94
  "process-log prints a shell run's stored stdout/stderr from the server: the whole log, its last n lines,",
94
95
  "or the lines matching a JavaScript regular expression. The log is not a file on the worker.",
96
+ "set-worker rebinds a stopped session to another connected worker: it publishes the current worker's workspace,",
97
+ "synchronizes the target, and the session resumes there. A running agent moves itself with its switch_worker tool.",
95
98
  ""
96
99
  ].join("\n");
97
100
  const SHARED_HELP_ENTRIES = [
@@ -230,6 +233,11 @@ const SHARED_HELP_ENTRIES = [
230
233
  usage: "session stop <session-id>",
231
234
  description: "Stop an active session generation."
232
235
  },
236
+ {
237
+ section: "sessions",
238
+ usage: "session set-worker <session-id> --worker <label>",
239
+ description: "Rebind a stopped session to another connected worker: publish the current worker's workspace, synchronize the target, and resume there. Refused while the agent runs; a running agent switches itself with switch_worker."
240
+ },
233
241
  {
234
242
  section: "sessions",
235
243
  usage: "[-s <session-id>] session claim <topic> [--ttl <duration>] [--note <text>] [--status|--renew|--release]",
@@ -798,6 +806,12 @@ function parseSessionRunCommand(options, args, env = process.env) {
798
806
  if (parsed.positionals.length > 1) throw new Error(`Unexpected session ${subcommand} argument: ${parsed.positionals[1]}`);
799
807
  return { kind: subcommand, sessionId };
800
808
  }
809
+ if (subcommand === "set-worker") {
810
+ const parsed = parseCommandOperands(commandArgs, /* @__PURE__ */ new Set(["--worker"]), /* @__PURE__ */ new Map(), "session set-worker");
811
+ const sessionId = requireValue(parsed.positionals[0], "Session id is required for `session set-worker`");
812
+ if (parsed.positionals.length > 1) throw new Error(`Unexpected session set-worker argument: ${parsed.positionals[1]}`);
813
+ return { kind: "set-worker", sessionId, worker: requireWorker(parsed.values) };
814
+ }
801
815
  if (subcommand === "prompt") {
802
816
  const parsed = parseCommandOperands(
803
817
  commandArgs,
@@ -2174,9 +2188,23 @@ async function dispatchSessionRunCommand(client, command) {
2174
2188
  });
2175
2189
  return { data: data2, human: renderSessionRunStatus(data2) };
2176
2190
  }
2191
+ if (command.kind === "set-worker") {
2192
+ const data2 = await client.sessions.setWorker(command.sessionId, { worker: command.worker });
2193
+ return { data: data2, human: renderSessionSetWorker(data2) };
2194
+ }
2177
2195
  const data = await client.sessions.stop(command.sessionId);
2178
2196
  return { data, human: renderSessionRunStatus(data) };
2179
2197
  }
2198
+ function renderSessionSetWorker(response) {
2199
+ const previous = typeof response.previousWorker === "string" && response.previousWorker.length > 0 ? response.previousWorker : null;
2200
+ const lines = [
2201
+ `Session: ${responseString(response, "sessionId") ?? "(unknown)"}`,
2202
+ `Worker: ${responseString(response, "worker") ?? "(unassigned)"}${previous ? ` (was ${previous})` : ""}`,
2203
+ "The session resumes on this worker; shell runs and sub-agents started earlier stay where they ran."
2204
+ ];
2205
+ return `${lines.join("\n")}
2206
+ `;
2207
+ }
2180
2208
  async function waitForSessionProvisioning(client, initial, options = {}) {
2181
2209
  const sessionId = requireValue(responseString(initial, "sessionId"), "Session start did not return a session id");
2182
2210
  const wait = options.wait ?? sleep;
@@ -3774,6 +3802,7 @@ export {
3774
3802
  renderSessionList,
3775
3803
  renderSessionRunStart,
3776
3804
  renderSessionRunStatus,
3805
+ renderSessionSetWorker,
3777
3806
  renderWorkerCapacity,
3778
3807
  renderWorkspaceStatus,
3779
3808
  renderWorkspaceSync,
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.130",
3
+ "version": "0.0.132",
4
4
  "type": "module"
5
5
  }
@@ -84,6 +84,10 @@ export type R5dctlSessionRunCommand = R5dctlSessionStartCommand | {
84
84
  } | {
85
85
  kind: "stop";
86
86
  sessionId: string;
87
+ } | {
88
+ kind: "set-worker";
89
+ sessionId: string;
90
+ worker: string;
87
91
  };
88
92
  export type R5dctlBranchCreateArgs = {
89
93
  source: R5dctlWorktreeSource;
@@ -312,6 +316,8 @@ export type R5dctlCommandResponse = {
312
316
  baselineCommit?: string | null;
313
317
  workspaceHead?: string | null;
314
318
  worker?: string;
319
+ /** The binding a `session set-worker` replaced; null when the session had none. */
320
+ previousWorker?: string | null;
315
321
  status?: string;
316
322
  runStatus?: unknown;
317
323
  /** Worker queue state while a new worktree provisions; older servers and workers omit it. */
@@ -347,6 +353,9 @@ export type R5dctlSessionRunClient = {
347
353
  delivery?: R5dctlPromptDelivery;
348
354
  }) => Promise<R5dctlCommandResponse>;
349
355
  stop: (sessionId: string) => Promise<R5dctlCommandResponse>;
356
+ setWorker: (sessionId: string, input: {
357
+ worker: string;
358
+ }) => Promise<R5dctlCommandResponse>;
350
359
  };
351
360
  };
352
361
  export declare function renderSessionRunStart(session: R5dctlCommandResponse): string;
@@ -356,6 +365,7 @@ export type R5dctlCommandDispatchResult = {
356
365
  human: string;
357
366
  };
358
367
  export declare function dispatchSessionRunCommand(client: R5dctlSessionRunClient, command: R5dctlSessionRunCommand): Promise<R5dctlCommandDispatchResult>;
368
+ export declare function renderSessionSetWorker(response: R5dctlCommandResponse): string;
359
369
  export declare function waitForSessionProvisioning(client: Pick<R5dctlSessionRunClient, "sessions">, initial: R5dctlCommandResponse, options?: {
360
370
  intervalMs?: number;
361
371
  wait?: (milliseconds: number) => Promise<unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/r5dctl",
3
- "version": "0.0.130",
3
+ "version": "0.0.132",
4
4
  "type": "module",
5
5
  "main": "./dist/cjs/cli.cjs",
6
6
  "module": "./dist/mjs/cli.mjs",
@@ -26,7 +26,7 @@
26
26
  "r5dctl": "dist/cjs/main.cjs"
27
27
  },
28
28
  "dependencies": {
29
- "@ricsam/r5d-api": "^0.0.130",
29
+ "@ricsam/r5d-api": "^0.0.132",
30
30
  "dotenv": "^17",
31
31
  "qrcode": "^1.5.4",
32
32
  "ws": "^8.18.3"