// Node screen recording command: invokes screen.record and writes returned media locally. import type { Command } from "commander"; import { defaultRuntime } from "../../runtime.js"; import { shortenHomePath } from "../../utils.js"; import { parseScreenRecordPayload, screenRecordTempPath, writeScreenRecordToFile, } from "../nodes-screen.js"; import { parseDurationMs } from "../parse-duration.js"; import { runNodesCommand } from "./cli-utils.js"; import { buildNodeInvokeParams, callNodesGatewayCli, nodesCallOpts, parseOptionalNodeFiniteNumber, parseOptionalNodeNonNegativeInteger, parseOptionalNodePositiveInteger, resolveCliNodeId, } from "./rpc.js"; import type { NodesRpcOpts } from "./types.js"; /** Register node screen recording commands. */ export function registerNodesScreenCommands(nodes: Command) { const screen = nodes .command("screen") .description("Capture screen recordings from a paired node"); nodesCallOpts( screen .command("record") .description("Capture a short screen recording from a node (prints the saved path)") .requiredOption("--node ", "Node id, name, or IP") .option("--screen ", "Screen index (0 = primary)", "0") .option("--duration ", "Clip duration (ms or 10s)", "10000") .option("--fps ", "Frames per second", "10") .option("--no-audio", "Disable microphone audio capture") .option("--out ", "Output path") .option("--invoke-timeout ", "Node invoke timeout in ms (default 120000)", "120000") .action(async (opts: NodesRpcOpts & { out?: string }) => { await runNodesCommand("screen record", async () => { const durationMs = parseDurationMs(opts.duration ?? ""); const screenIndex = parseOptionalNodeNonNegativeInteger(opts.screen ?? "0", "--screen"); const fps = parseOptionalNodeFiniteNumber(opts.fps ?? "10", "--fps", { minExclusive: 0, }); const timeoutMs = parseOptionalNodePositiveInteger( opts.invokeTimeout, "--invoke-timeout", ); const nodeId = await resolveCliNodeId(opts, opts.node ?? ""); const invokeParams = buildNodeInvokeParams({ nodeId, command: "screen.record", params: { durationMs: Number.isFinite(durationMs) ? durationMs : undefined, screenIndex: Number.isFinite(screenIndex) ? screenIndex : undefined, fps: Number.isFinite(fps) ? fps : undefined, format: "mp4", includeAudio: opts.audio !== false, }, timeoutMs, }); const raw = await callNodesGatewayCli("node.invoke", opts, invokeParams); const res = typeof raw === "object" && raw !== null ? (raw as { payload?: unknown }) : {}; const parsed = parseScreenRecordPayload(res.payload); const filePath = opts.out ?? screenRecordTempPath({ ext: parsed.format || "mp4" }); const written = await writeScreenRecordToFile(filePath, parsed.base64); if (opts.json) { defaultRuntime.writeJson({ file: { path: written.path, durationMs: parsed.durationMs, fps: parsed.fps, screenIndex: parsed.screenIndex, hasAudio: parsed.hasAudio, }, }); return; } defaultRuntime.log(shortenHomePath(written.path)); }); }), { timeoutMs: 180_000 }, ); }