feat: queue codex remote control actions

This commit is contained in:
AI Bot
2026-06-04 17:12:23 +08:00
parent b93bc22160
commit 025e749618
14 changed files with 958 additions and 500 deletions

View File

@@ -0,0 +1,95 @@
import { NextRequest } from "next/server";
import { jsonNoStore } from "@/lib/api-response";
import { buildRequestAuditMeta } from "@/lib/boss-audit";
import { requireRequestSession } from "@/lib/boss-auth";
import {
appendPermissionAuditLog,
queueCodexRemoteControlTask,
readState,
type CodexRemoteControlAction,
} from "@/lib/boss-data";
import { canAccessDevice } from "@/lib/boss-permissions";
function normalizeAction(value: unknown): CodexRemoteControlAction | null {
return value === "start" || value === "stop" ? value : null;
}
async function recordDenied(input: {
actorAccount: string;
deviceId: string;
reason: string;
request: NextRequest;
}) {
const auditMeta = buildRequestAuditMeta(input.request);
await appendPermissionAuditLog({
actorAccount: input.actorAccount,
action: "task.denied",
deviceId: input.deviceId,
permissions: ["computer.control"],
detail: input.reason,
...auditMeta,
});
}
export async function POST(
request: NextRequest,
context: { params: Promise<{ deviceId: string }> },
) {
const session = await requireRequestSession(request);
if (!session) {
return jsonNoStore({ ok: false, message: "UNAUTHORIZED" }, { status: 401 });
}
const { deviceId } = await context.params;
const body = (await request.json().catch(() => ({}))) as {
action?: unknown;
confirmed?: unknown;
reason?: unknown;
};
const action = normalizeAction(body.action);
if (!action) {
return jsonNoStore({ ok: false, message: "CODEX_REMOTE_CONTROL_ACTION_INVALID" }, { status: 400 });
}
if (body.confirmed !== true) {
return jsonNoStore({ ok: false, message: "CODEX_REMOTE_CONTROL_CONFIRMATION_REQUIRED" }, { status: 400 });
}
const state = await readState();
const device = state.devices.find((item) => item.id === deviceId);
if (!device) {
return jsonNoStore({ ok: false, message: "DEVICE_NOT_FOUND" }, { status: 404 });
}
if (device.status !== "online") {
await recordDenied({
actorAccount: session.account,
deviceId,
reason: `codex_remote_control:${action}:device_offline`,
request,
});
return jsonNoStore({ ok: false, message: "DEVICE_OFFLINE" }, { status: 409 });
}
if (!canAccessDevice(state, session, deviceId, "computer.control")) {
await recordDenied({
actorAccount: session.account,
deviceId,
reason: `codex_remote_control:${action}:forbidden`,
request,
});
return jsonNoStore({ ok: false, message: "FORBIDDEN" }, { status: 403 });
}
const task = await queueCodexRemoteControlTask({
deviceId,
action,
requestedBy: session.displayName || session.account,
requestedByAccount: session.account,
reason: typeof body.reason === "string" ? body.reason : undefined,
auditMeta: buildRequestAuditMeta(request),
});
return jsonNoStore({
ok: true,
task,
message: action === "start" ? "CODEX_REMOTE_CONTROL_START_QUEUED" : "CODEX_REMOTE_CONTROL_STOP_QUEUED",
});
}

View File

@@ -477,8 +477,11 @@ export type MasterAgentTaskType =
| "group_dispatch_plan"
| "dispatch_execution"
| "device_import_resolution"
| "device_maintenance"
| "browser_control"
| "desktop_control";
export type DeviceMaintenanceKind = "codex_remote_control";
export type CodexRemoteControlAction = "start" | "stop";
export type ComputerControlIntentCategory =
| "discussion_only"
| "project_development"
@@ -1353,6 +1356,8 @@ export interface MasterAgentTask {
deviceImportDraftId?: string;
deviceImportCandidateId?: string;
deviceImportCandidateFolderName?: string;
maintenanceKind?: DeviceMaintenanceKind;
codexRemoteControlAction?: CodexRemoteControlAction;
projectUnderstandingTargetProjectId?: string;
projectUnderstandingReason?: "heartbeat_activity" | "thread_reply";
projectUnderstandingReplyProjectId?: string;
@@ -4769,6 +4774,12 @@ export function migrateBossState(raw: Partial<BossState> | undefined): BossState
deviceImportDraftId: task.deviceImportDraftId,
deviceImportCandidateId: task.deviceImportCandidateId,
deviceImportCandidateFolderName: task.deviceImportCandidateFolderName,
maintenanceKind:
task.maintenanceKind === "codex_remote_control" ? task.maintenanceKind : undefined,
codexRemoteControlAction:
task.codexRemoteControlAction === "start" || task.codexRemoteControlAction === "stop"
? task.codexRemoteControlAction
: undefined,
projectUnderstandingTargetProjectId: task.projectUnderstandingTargetProjectId,
projectUnderstandingReason:
task.projectUnderstandingReason === "heartbeat_activity" || task.projectUnderstandingReason === "thread_reply"
@@ -6020,6 +6031,14 @@ function normalizeExecutionProgressStreamEvents(
}
function defaultExecutionProgressStepTexts(task: Pick<MasterAgentTask, "taskType" | "relayViaMasterAgent">) {
if (task.taskType === "device_maintenance") {
return [
"接收设备维护指令",
"确认设备权限和绑定状态",
"执行本机维护动作",
"回写维护结果",
];
}
if (task.taskType === "browser_control") {
return [
"接收远程控制指令",
@@ -6098,6 +6117,9 @@ function normalizeExecutionProgressSteps(
}
function resolveTaskExecutionProgressProjectId(task: Pick<MasterAgentTask, "projectId" | "taskType" | "targetProjectId">) {
if (task.taskType === "device_maintenance") {
return task.projectId?.trim() || "";
}
if (task.taskType === "browser_control" || task.taskType === "desktop_control") {
return task.projectId?.trim() || "";
}
@@ -6111,6 +6133,9 @@ function resolveTaskExecutionProgressProjectId(task: Pick<MasterAgentTask, "proj
}
function shouldShowTaskExecutionProgress(task: Pick<MasterAgentTask, "projectId" | "taskType" | "targetProjectId" | "targetThreadId">) {
if (task.taskType === "device_maintenance") {
return Boolean(task.projectId?.trim());
}
if (task.taskType === "browser_control" || task.taskType === "desktop_control") {
return Boolean(task.projectId?.trim());
}
@@ -6135,7 +6160,11 @@ function buildExecutionProgressSnapshot(
}
const normalizedStatus = normalizeExecutionProgressStatus(status);
const steps = normalizeExecutionProgressSteps(task, normalizedStatus, input?.steps);
const nativeRemoteControl = task.taskType === "browser_control" || task.taskType === "desktop_control";
const nativeRemoteControl =
task.taskType === "browser_control" ||
task.taskType === "desktop_control" ||
task.taskType === "device_maintenance";
const maintenanceControl = task.taskType === "device_maintenance";
return {
taskId: task.taskId,
projectId,
@@ -6145,7 +6174,7 @@ function buildExecutionProgressSnapshot(
runtimeKind: task.runtimeKind,
controlPlatform: task.controlPlatform,
computerUseProvider: task.computerUseProvider,
title: nativeRemoteControl ? "远程控制进度" : "进度",
title: maintenanceControl ? "设备维护进度" : nativeRemoteControl ? "远程控制进度" : "进度",
status: normalizedStatus,
steps,
branch: nativeRemoteControl ? undefined : normalizeExecutionProgressBranch(input?.branch),
@@ -8882,6 +8911,8 @@ export async function queueMasterAgentTask(payload: {
orchestrationBackendLabel?: string;
deviceImportCandidateId?: string;
deviceImportCandidateFolderName?: string;
maintenanceKind?: DeviceMaintenanceKind;
codexRemoteControlAction?: CodexRemoteControlAction;
projectUnderstandingTargetProjectId?: string;
projectUnderstandingReason?: "heartbeat_activity" | "thread_reply";
projectUnderstandingReplyProjectId?: string;
@@ -8956,6 +8987,12 @@ export async function queueMasterAgentTask(payload: {
orchestrationBackendLabel: payload.orchestrationBackendLabel,
deviceImportCandidateId: payload.deviceImportCandidateId,
deviceImportCandidateFolderName: payload.deviceImportCandidateFolderName,
maintenanceKind:
payload.maintenanceKind === "codex_remote_control" ? payload.maintenanceKind : undefined,
codexRemoteControlAction:
payload.codexRemoteControlAction === "start" || payload.codexRemoteControlAction === "stop"
? payload.codexRemoteControlAction
: undefined,
projectUnderstandingTargetProjectId: payload.projectUnderstandingTargetProjectId,
projectUnderstandingReason: payload.projectUnderstandingReason,
projectUnderstandingReplyProjectId: payload.projectUnderstandingReplyProjectId,
@@ -9027,6 +9064,55 @@ export async function queueMasterAgentTask(payload: {
return task;
}
export async function queueCodexRemoteControlTask(input: {
deviceId: string;
action: CodexRemoteControlAction;
requestedBy: string;
requestedByAccount: string;
reason?: string;
auditMeta?: PermissionAuditMeta;
}) {
if (input.action !== "start" && input.action !== "stop") {
throw new Error("CODEX_REMOTE_CONTROL_ACTION_INVALID");
}
const actionLabel = input.action === "start" ? "启动" : "停止";
const reason = trimToDefined(input.reason) ?? `${actionLabel} Codex Remote Control。`;
const task = await queueMasterAgentTask({
projectId: "master-agent",
taskType: "device_maintenance",
requestMessageId: randomToken("msg-maintenance"),
requestText: reason,
executionPrompt: `设备维护:${actionLabel} Codex Remote Control。`,
requestedBy: input.requestedBy,
requestedByAccount: input.requestedByAccount,
authorizedDeviceIds: [input.deviceId],
requiredPermissions: ["computer.control"],
deviceId: input.deviceId,
maintenanceKind: "codex_remote_control",
codexRemoteControlAction: input.action,
runtimeKind: "codex-thread-runtime",
confirmationPolicy: "strong_confirm",
requiresUserConfirmation: true,
confirmationScopeKey: `device:${input.deviceId}:codex_remote_control`,
});
await appendPermissionAuditLog({
actorAccount: input.requestedByAccount,
action: "task.authorized",
deviceId: input.deviceId,
permissions: ["computer.control"],
detail: `codex_remote_control:${input.action}`,
ipAddress: input.auditMeta?.ipAddress,
userAgent: input.auditMeta?.userAgent,
requestId: input.auditMeta?.requestId,
afterJson: {
taskId: task.taskId,
maintenanceKind: "codex_remote_control",
codexRemoteControlAction: input.action,
},
});
return task;
}
export async function createDispatchPlan(input: {
groupProjectId: string;
requestMessageId: string;