@awebai/oats 0.22.10 → 0.22.14

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/lib/servers.mjs CHANGED
@@ -185,7 +185,9 @@ export function runRemote(target, oatsArgs, io = {}) {
185
185
  let stderr = "";
186
186
  let status = 0;
187
187
  try {
188
- stdout = exec(bin, argv, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 16 * 1024 * 1024, timeout: io.timeoutMs || 300000 });
188
+ // `io.input` (a Buffer) streams to the remote command's stdin: the only
189
+ // way bytes reach a host, as a quoted argument never could.
190
+ stdout = exec(bin, argv, { encoding: "utf8", stdio: [io.input === undefined ? "ignore" : "pipe", "pipe", "pipe"], ...(io.input === undefined ? {} : { input: io.input }), maxBuffer: 16 * 1024 * 1024, timeout: io.timeoutMs || 300000 });
189
191
  } catch (e) {
190
192
  stdout = String(e.stdout || "");
191
193
  stderr = String(e.stderr || e.message || "");
@@ -258,6 +260,8 @@ export function checkRemoteSupport(remote, target, oatsArgs, roster) {
258
260
  : `it does not advertise what it supports (kernels before 0.22.2 do not), so only pi and claude on tmux with no launch options are assumed of it; upgrade it there to use more`;
259
261
  const refuse = (what) => { throw serverError("E_REMOTE_INCOMPATIBLE", `remote oats ${remote.version} at ${target.sshHost}: ${what} was not established as supported there (${supports})`); };
260
262
  if (runtime && !remote.runtimes.includes(runtime)) refuse(`runtime ${runtime}${flagOf("--runtime") ? "" : ` (the default of soul ${agent} there)`}`);
263
+ // A wake schedule at spawn is saved by the host: it must advertise schedules.
264
+ if (["--wake-json", "--wake-every", "--wake-cron"].some((f) => oatsArgs.includes(f)) && !remote.features.includes("schedule")) refuse("a wake schedule at spawn (the host must advertise the schedule feature)");
261
265
  const backend = flagOf("--backend");
262
266
  if (backend && !remote.sessionBackends.includes(backend)) refuse(`session backend ${backend}`);
263
267
  if (oatsArgs.includes("--yolo") && !remote.launchOptions.includes("yolo")) refuse("the yolo launch option");
@@ -632,6 +636,20 @@ export function startRemote(serverId, { instance, home, model } = {}, io = {}) {
632
636
  return { envelope: envelope.ok ? { ...envelope, result: { ...envelope.result, server: serverId, instance: instance || route.snapshot?.instance || envelope.result.instance } } : envelope, stderr, route };
633
637
  }
634
638
 
639
+ /** `oats schedule ...` on the execution host: schedules are host-owned, so
640
+ * every subcommand runs in the server's registered workspace; refused
641
+ * before any remote mutation when the remote kernel does not advertise
642
+ * the schedule feature. The envelope is relayed as is. */
643
+ export function scheduleRemote(serverId, oatsArgs, io = {}) {
644
+ const server = io.server || getServer(serverId);
645
+ const target = targetOf(server);
646
+ const remote = checkRemote(target, io);
647
+ if (!remote.features.includes("schedule")) throw serverError("E_REMOTE_INCOMPATIBLE", `remote oats ${remote.version} at ${target.sshHost} does not advertise schedules; upgrade it there`);
648
+ const args = ["schedule", ...oatsArgs.filter((a) => a !== "--json"), "--dir", target.workspace, "--json"];
649
+ const { envelope, stderr } = runRemote(target, args, io);
650
+ return { envelope: envelope.ok ? { ...envelope, result: { ...envelope.result, server: serverId } } : envelope, stderr, target };
651
+ }
652
+
635
653
  export function attachArgv(serverId, { instance, home } = {}, io = {}) {
636
654
  const { target, home: remoteHome } = resolveRoute(serverId, { instance, home }, "session attach");
637
655
  if (!io.skipVersionCheck) requireSessionRemote(target, io);
@@ -26,6 +26,10 @@ export function prepareSessionViewer(target, { exec = execFileSync } = {}) {
26
26
  // when this agent retires. Disable window navigation in the viewer only.
27
27
  for (const name of ["prefix", "prefix2"]) run(["set-option", "-t", viewer, name, "None"]);
28
28
  run(["set-option", "-t", viewer, "key-table", "oatsview-locked"]);
29
+ // The viewer shows one agent; its status line only repeats that name.
30
+ // Session-scoped on the temporary viewer: the agents' own session and
31
+ // the operator's tmux settings are untouched.
32
+ run(["set-option", "-t", viewer, "status", "off"]);
29
33
  run(["unbind-key", "-a", "-q", "-T", "oatsview-locked"]);
30
34
  run(["bind-key", "-T", "oatsview-locked", "WheelUpPane", "if-shell", "-F", "#{||:#{pane_in_mode},#{mouse_any_flag}}", "send-keys -M", "copy-mode -e; send-keys -M"]);
31
35
  run(["set-option", "-t", viewer, "mouse", "on"]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awebai/oats",
3
- "version": "0.22.10",
3
+ "version": "0.22.14",
4
4
  "description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
5
5
  "keywords": [
6
6
  "agents",
@@ -44,7 +44,8 @@
44
44
  "packages/record/package.json"
45
45
  ],
46
46
  "dependencies": {
47
- "@awebai/pi": "^0.2.1"
47
+ "@awebai/pi": "^0.2.1",
48
+ "croner": "10.0.1"
48
49
  },
49
50
  "bin": {
50
51
  "oats": "./bin/oats.mjs"