mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 13:51:26 +00:00
feat: add mac node screen recording and ssh tunnel
This commit is contained in:
@@ -12,6 +12,11 @@ import {
|
||||
canvasSnapshotTempPath,
|
||||
parseCanvasSnapshotPayload,
|
||||
} from "./nodes-canvas.js";
|
||||
import {
|
||||
parseScreenRecordPayload,
|
||||
screenRecordTempPath,
|
||||
writeScreenRecordToFile,
|
||||
} from "./nodes-screen.js";
|
||||
import { parseDurationMs } from "./parse-duration.js";
|
||||
|
||||
type NodesRpcOpts = {
|
||||
@@ -29,6 +34,8 @@ type NodesRpcOpts = {
|
||||
maxWidth?: string;
|
||||
quality?: string;
|
||||
duration?: string;
|
||||
screen?: string;
|
||||
fps?: string;
|
||||
audio?: boolean;
|
||||
};
|
||||
|
||||
@@ -760,4 +767,97 @@ export function registerNodesCli(program: Command) {
|
||||
}),
|
||||
{ timeoutMs: 90_000 },
|
||||
);
|
||||
|
||||
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 MEDIA:<path>)",
|
||||
)
|
||||
.requiredOption("--node <idOrNameOrIp>", "Node id, name, or IP")
|
||||
.option("--screen <index>", "Screen index (0 = primary)", "0")
|
||||
.option("--duration <ms|10s>", "Clip duration (ms or 10s)", "10000")
|
||||
.option("--fps <fps>", "Frames per second", "10")
|
||||
.option("--out <path>", "Output path")
|
||||
.option(
|
||||
"--invoke-timeout <ms>",
|
||||
"Node invoke timeout in ms (default 120000)",
|
||||
"120000",
|
||||
)
|
||||
.action(async (opts: NodesRpcOpts & { out?: string }) => {
|
||||
try {
|
||||
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
|
||||
const durationMs = parseDurationMs(opts.duration ?? "");
|
||||
const screenIndex = Number.parseInt(String(opts.screen ?? "0"), 10);
|
||||
const fps = Number.parseFloat(String(opts.fps ?? "10"));
|
||||
const timeoutMs = opts.invokeTimeout
|
||||
? Number.parseInt(String(opts.invokeTimeout), 10)
|
||||
: undefined;
|
||||
|
||||
const invokeParams: Record<string, unknown> = {
|
||||
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",
|
||||
},
|
||||
idempotencyKey: randomIdempotencyKey(),
|
||||
};
|
||||
if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs)) {
|
||||
invokeParams.timeoutMs = timeoutMs;
|
||||
}
|
||||
|
||||
const raw = (await callGatewayCli(
|
||||
"node.invoke",
|
||||
opts,
|
||||
invokeParams,
|
||||
)) as unknown;
|
||||
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.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
file: {
|
||||
path: written.path,
|
||||
durationMs: parsed.durationMs,
|
||||
fps: parsed.fps,
|
||||
screenIndex: parsed.screenIndex,
|
||||
},
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
defaultRuntime.log(`MEDIA:${written.path}`);
|
||||
} catch (err) {
|
||||
defaultRuntime.error(`nodes screen record failed: ${String(err)}`);
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
}),
|
||||
{ timeoutMs: 180_000 },
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user