feat: ship enterprise control and desktop governance

This commit is contained in:
AI Bot
2026-05-11 14:59:26 +08:00
parent 0757d07521
commit a311280238
285 changed files with 48574 additions and 2428 deletions

View File

@@ -0,0 +1,435 @@
#!/usr/bin/env node
import { spawn } from "node:child_process";
import { mkdir, writeFile } from "node:fs/promises";
import path from "node:path";
import {
buildDialogAuditEntry,
buildDialogInterventionResult,
evaluateDialogSnapshot,
readDialogSnapshotFromEnv,
} from "../local-agent/desktop-dialog-guard.mjs";
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 normalizePayload(raw) {
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
return {
ok: false,
error: "INVALID_COMPUTER_USE_PAYLOAD: expected object",
};
}
return {
ok: true,
payload: parsed,
};
} catch {
return {
ok: false,
error: "INVALID_COMPUTER_USE_PAYLOAD: invalid json",
};
}
}
function detectTargetApp(objective) {
const text = String(objective || "").toLowerCase();
const candidates = [
["微信", ["微信", "wechat"]],
["飞书", ["飞书", "lark", "feishu"]],
["Telegram", ["telegram"]],
["QQ", ["qq"]],
["Finder", ["finder", "访达"]],
["系统设置", ["系统设置", "system settings", "settings"]],
["Chrome", ["chrome", "谷歌浏览器"]],
["Safari", ["safari"]],
];
for (const [label, aliases] of candidates) {
if (aliases.some((alias) => text.includes(alias.toLowerCase()))) {
return label;
}
}
return undefined;
}
function detectDesktopAction(objective) {
const text = String(objective || "").toLowerCase();
if (text.includes("系统设置") || text.includes("settings")) {
return "open_settings";
}
if (text.includes("访达") || text.includes("finder")) {
return "open_finder";
}
if (text.includes("微信") || text.includes("wechat")) {
return "open_wechat";
}
if (text.includes("飞书") || text.includes("lark") || text.includes("feishu")) {
return "open_feishu";
}
if (text.includes("telegram")) {
return "open_telegram";
}
if (text.includes("qq")) {
return "open_qq";
}
return "open_app";
}
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") ||
text.includes("submit")
);
}
function parseArgs(value) {
return String(value || "")
.trim()
.split(/\s+/)
.filter(Boolean);
}
function parseArgsJson(value) {
const raw = String(value || "").trim();
if (!raw) {
return undefined;
}
try {
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed.map((item) => String(item)).filter(Boolean) : undefined;
} catch {
return undefined;
}
}
function resolveOpenAppPrefixArgs(command) {
const rawJson = String(process.env.BOSS_COMPUTER_USE_OPEN_APP_ARGS_JSON || "").trim();
const rawArgs = String(process.env.BOSS_COMPUTER_USE_OPEN_APP_ARGS || "").trim();
if (rawJson || rawArgs) {
return parseArgsJson(rawJson) ?? parseArgs(rawArgs);
}
return path.basename(command || "").toLowerCase() === "open" ? ["-a"] : [];
}
async function writeArtifact(payload) {
const artifactDir = String(process.env.BOSS_CONTROL_ARTIFACT_DIR || "").trim();
if (!artifactDir) {
return [];
}
await mkdir(artifactDir, { recursive: true });
const requestId =
typeof payload.requestId === "string" && payload.requestId.trim()
? payload.requestId.trim()
: `desktop-${Date.now()}`;
const artifactPath = path.join(artifactDir, `${requestId}.json`);
await writeFile(artifactPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
return [
{
kind: "json",
path: artifactPath,
},
];
}
function getDesktopAutomationMode() {
const raw = String(process.env.BOSS_COMPUTER_USE_MODE || "").trim().toLowerCase();
if (raw === "off" || raw === "open" || raw === "osascript" || raw === "auto") {
return raw;
}
return "auto";
}
function getDialogGuardEnabled() {
return String(process.env.BOSS_DIALOG_GUARD_ENABLED || "").trim().toLowerCase() === "true";
}
function readDialogGuardSnapshot() {
return readDialogSnapshotFromEnv(process.env, process.env.BOSS_DIALOG_GUARD_PLATFORM || process.platform);
}
function resolveDialogGuardActionCommand(platform) {
const normalizedPlatform = String(platform || "").trim();
if (normalizedPlatform === "darwin") {
const command = String(process.env.BOSS_MAC_DIALOG_GUARD_ACTION_COMMAND || "").trim();
if (command) {
return {
command,
args: parseArgsJson(process.env.BOSS_MAC_DIALOG_GUARD_ACTION_ARGS_JSON) ??
parseArgs(process.env.BOSS_MAC_DIALOG_GUARD_ACTION_ARGS),
};
}
}
if (normalizedPlatform === "win32") {
const command = String(process.env.BOSS_WINDOWS_DIALOG_GUARD_ACTION_COMMAND || "").trim();
if (command) {
return {
command,
args: parseArgsJson(process.env.BOSS_WINDOWS_DIALOG_GUARD_ACTION_ARGS_JSON) ??
parseArgs(process.env.BOSS_WINDOWS_DIALOG_GUARD_ACTION_ARGS),
};
}
}
const command = String(process.env.BOSS_DIALOG_GUARD_ACTION_COMMAND || "").trim();
if (!command) {
return undefined;
}
return {
command,
args: parseArgsJson(process.env.BOSS_DIALOG_GUARD_ACTION_ARGS_JSON) ??
parseArgs(process.env.BOSS_DIALOG_GUARD_ACTION_ARGS),
};
}
function buildDialogGuardActionArgs(snapshot, decision) {
return [
"--platform",
snapshot.platform,
"--app",
snapshot.appName,
"--dialog-id",
decision.signature?.id || "",
"--action",
decision.action || "",
"--button",
decision.button || "",
].filter((item) => item !== "");
}
async function applyDialogGuardAutoAction(snapshot, decision) {
if (decision?.disposition !== "auto_action") {
return false;
}
const actionCommand = resolveDialogGuardActionCommand(snapshot.platform);
if (!actionCommand?.command) {
return false;
}
await runCommand(actionCommand.command, [
...(actionCommand.args || []),
...buildDialogGuardActionArgs(snapshot, decision),
]);
return true;
}
async function runDialogGuardPreflight(payload) {
if (!getDialogGuardEnabled()) {
return {};
}
const snapshot = readDialogGuardSnapshot();
if (!snapshot) {
return {};
}
const decision = evaluateDialogSnapshot(snapshot);
if (decision.disposition === "needs_user_action") {
return {
pausedResult: buildDialogInterventionResult({
requestId: typeof payload.requestId === "string" ? payload.requestId : undefined,
snapshot,
decision,
}),
decision,
snapshot,
};
}
const actionApplied = await applyDialogGuardAutoAction(snapshot, decision);
const auditEntry = buildDialogAuditEntry({
requestId: typeof payload.requestId === "string" ? payload.requestId : undefined,
snapshot,
decision,
});
auditEntry.actionApplied = actionApplied;
return {
decision,
snapshot,
actionApplied,
auditEntry,
};
}
function escapeAppleScriptString(value) {
return String(value || "")
.replaceAll("\\", "\\\\")
.replaceAll('"', '\\"');
}
function buildAppleScript(targetApp, objective) {
const app = escapeAppleScriptString(targetApp);
const script = [
`tell application "${app}"`,
"activate",
"end tell",
];
const textToType = extractQuotedText(objective);
if (textToType) {
script.push("delay 0.2");
script.push("tell application \"System Events\"");
script.push(`keystroke "${escapeAppleScriptString(textToType)}"`);
if (shouldSubmitAfterTyping(objective)) {
script.push("key code 36");
}
script.push("end tell");
}
return script.join("\n");
}
async function runCommand(command, args) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
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() || `computer use open exit code ${code}`));
return;
}
resolve({ stdout: stdout.trim(), stderr: stderr.trim() });
});
});
}
async function runAppleScript(targetApp, objective) {
if (!targetApp) {
return undefined;
}
const script = buildAppleScript(targetApp, objective);
await runCommand("osascript", ["-e", script]);
return `osascript activated ${targetApp}`;
}
const raw = await readStdin();
const normalized = normalizePayload(raw);
if (!normalized.ok) {
writeJson({
status: "failed",
error: normalized.error,
});
process.exit(0);
}
const payload = normalized.payload;
const objective =
typeof payload.objective === "string" && payload.objective.trim()
? payload.objective.trim()
: "桌面控制 smoke 链路正常";
const targetApp = detectTargetApp(objective);
const desktopAction = detectDesktopAction(objective);
const riskLevel =
typeof payload.context?.riskLevel === "string" && payload.context.riskLevel.trim()
? payload.context.riskLevel.trim()
: "unknown";
const dryRun = payload.context?.dryRun === true;
let dialogGuardState = {};
try {
dialogGuardState = await runDialogGuardPreflight(payload);
} catch (error) {
writeJson({
status: "failed",
requestId: typeof payload.requestId === "string" ? payload.requestId : undefined,
error: error?.message || "DIALOG_GUARD_FAILED",
});
process.exit(0);
}
if (dialogGuardState.pausedResult) {
writeJson(dialogGuardState.pausedResult);
process.exit(0);
}
let action = targetApp ? desktopAction : "computer_use_smoke";
const configuredMode = getDesktopAutomationMode();
const automationMode =
configuredMode === "auto" ? (process.platform === "darwin" ? "osascript" : "open") : configuredMode;
if (targetApp && !dryRun) {
if (automationMode === "osascript") {
await runAppleScript(targetApp, objective);
action = `${desktopAction}_executed`;
} else if (automationMode !== "off") {
const command = String(process.env.BOSS_COMPUTER_USE_OPEN_APP_COMMAND || "").trim() || "open";
const prefixArgs = resolveOpenAppPrefixArgs(command);
await runCommand(command, [...prefixArgs, targetApp]);
action = `${desktopAction}_executed`;
}
}
const artifacts = await writeArtifact({
requestKind: payload.requestKind,
requestId: payload.requestId,
action,
objective,
targetApp,
typedText: extractQuotedText(objective),
dryRun,
riskLevel,
mode: automationMode,
dialogGuard: dialogGuardState.auditEntry,
capturedAt: new Date().toISOString(),
});
writeJson({
status: "completed",
requestId: typeof payload.requestId === "string" ? payload.requestId : undefined,
replyBody: `桌面控制已完成:${objective}`,
executionSummary: `${action} completed (risk=${riskLevel}, mode=${automationMode}${
dialogGuardState.decision?.disposition ? `, dialogGuard=${dialogGuardState.decision.disposition}` : ""
})`,
targetApp,
typedText: extractQuotedText(objective),
dialogGuard: dialogGuardState.decision
? {
disposition: dialogGuardState.decision.disposition,
kind: dialogGuardState.decision.kind,
risk: dialogGuardState.decision.risk,
action: dialogGuardState.decision.action,
button: dialogGuardState.decision.button,
actionApplied: dialogGuardState.actionApplied,
}
: undefined,
artifacts,
});