feat: route desktop control to authorized devices

This commit is contained in:
AI Bot
2026-05-12 12:15:43 +08:00
parent bc199dcf5c
commit 4de64ac01c
8 changed files with 407 additions and 5 deletions

View File

@@ -0,0 +1,202 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
function writeJson(payload) {
process.stdout.write(`${JSON.stringify(payload)}\n`);
}
async function readStdin() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
}
return chunks.join("").trim();
}
function parsePayload(raw) {
try {
const parsed = JSON.parse(raw || "{}");
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
throw new Error("expected object");
}
return parsed;
} catch {
return null;
}
}
function envString(name) {
return String(process.env[name] || "").trim();
}
function envBoolean(name) {
return envString(name).toLowerCase() === "true";
}
function detectTargetApp(objective) {
const text = String(objective || "").toLowerCase();
const candidates = [
["Chrome", ["chrome", "谷歌浏览器"]],
["Safari", ["safari"]],
["Finder", ["finder", "访达"]],
["System Settings", ["system settings", "系统设置", "设置"]],
["QQ", ["qq"]],
["WeChat", ["wechat", "微信"]],
["Telegram", ["telegram"]],
];
for (const [name, aliases] of candidates) {
if (aliases.some((alias) => text.includes(alias.toLowerCase()))) {
return name;
}
}
return "Finder";
}
function extractQuotedText(objective) {
const text = String(objective || "");
const patterns = [
/[“"]([^“”"]+)[”"]/,
/[「『]([^」』]+)[」』]/,
/输入[:]\s*([^\n。;]+)/,
/打字[:]\s*([^\n。;]+)/,
];
for (const pattern of patterns) {
const match = text.match(pattern);
const value = match?.[1]?.trim();
if (value) return value;
}
return undefined;
}
function shouldSubmitAfterTyping(objective) {
const text = String(objective || "").toLowerCase();
return text.includes("发送") || text.includes("提交") || text.includes("回车") || text.includes("enter");
}
function escapeAppleScriptString(value) {
return String(value || "").replaceAll("\\", "\\\\").replaceAll('"', '\\"');
}
function buildAppleScript(targetApp, objective) {
const lines = [
`tell application "${escapeAppleScriptString(targetApp)}"`,
"activate",
"end tell",
];
const typedText = extractQuotedText(objective);
if (typedText) {
lines.push("delay 0.2");
lines.push('tell application "System Events"');
lines.push(`keystroke "${escapeAppleScriptString(typedText)}"`);
if (shouldSubmitAfterTyping(objective)) {
lines.push("key code 36");
}
lines.push("end tell");
}
return lines.join("\n");
}
function runCommand(command, args, env = {}) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
env: {
...process.env,
...env,
},
stdio: ["ignore", "pipe", "pipe"],
});
let stdout = "";
let stderr = "";
child.stdout.setEncoding("utf8");
child.stderr.setEncoding("utf8");
child.stdout.on("data", (chunk) => {
stdout += chunk;
});
child.stderr.on("data", (chunk) => {
stderr += chunk;
});
child.on("error", reject);
child.on("close", (code) => {
if (code !== 0) {
reject(new Error(stderr.trim() || `ssh computer use exited with ${code}`));
return;
}
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
});
});
}
async function runRemoteAppleScript(script) {
const host = envString("BOSS_SSH_CONTROL_HOST");
const user = envString("BOSS_SSH_CONTROL_USER");
const port = envString("BOSS_SSH_CONTROL_PORT") || "22";
const password = envString("BOSS_SSH_CONTROL_PASSWORD");
if (!host) throw new Error("SSH_CONTROL_HOST_REQUIRED");
if (!user) throw new Error("SSH_CONTROL_USER_REQUIRED");
const sshTarget = `${user}@${host}`;
const encodedScript = Buffer.from(script, "utf8").toString("base64");
const remoteCommand = `printf '%s' '${encodedScript}' | base64 -D | osascript`;
const sshArgs = [
"-o",
"StrictHostKeyChecking=no",
"-o",
"ConnectTimeout=8",
"-o",
"PreferredAuthentications=password",
"-o",
"PubkeyAuthentication=no",
"-o",
"NumberOfPasswordPrompts=1",
"-p",
port,
sshTarget,
remoteCommand,
];
if (password) {
await runCommand("sshpass", ["-e", "ssh", ...sshArgs], { SSHPASS: password });
return;
}
await runCommand("ssh", sshArgs);
}
const payload = parsePayload(await readStdin());
if (!payload) {
writeJson({ status: "failed", error: "INVALID_SSH_COMPUTER_USE_PAYLOAD" });
process.exit(0);
}
const requestId = typeof payload.requestId === "string" ? payload.requestId : undefined;
const objective = typeof payload.objective === "string" && payload.objective.trim()
? payload.objective.trim()
: "远程桌面控制 smoke 链路测试";
const targetApp = detectTargetApp(objective);
const typedText = extractQuotedText(objective);
try {
if (!envString("BOSS_SSH_CONTROL_HOST")) {
throw new Error("SSH_CONTROL_HOST_REQUIRED");
}
if (!envString("BOSS_SSH_CONTROL_USER")) {
throw new Error("SSH_CONTROL_USER_REQUIRED");
}
const appleScript = buildAppleScript(targetApp, objective);
if (!envBoolean("BOSS_SSH_CONTROL_DRY_RUN")) {
await runRemoteAppleScript(appleScript);
}
writeJson({
status: "completed",
requestId,
replyBody: `SSH 桌面控制已完成:${objective}`,
executionSummary: `ssh osascript ${envBoolean("BOSS_SSH_CONTROL_DRY_RUN") ? "dry-run" : "executed"} (${targetApp})`,
targetApp,
typedText,
});
} catch (error) {
writeJson({
status: "failed",
requestId,
error: error instanceof Error ? error.message : "SSH_COMPUTER_USE_FAILED",
});
}