Feature: MCP 远程控制(remote_open/close/keyboard/mouse/clipboard) #4
@@ -3851,6 +3851,10 @@ bool CMcpServer::BeginScreenCtrlOpen(uint64_t device_id, const std::string& sess
|
|||||||
s.sessionId = sessionId;
|
s.sessionId = sessionId;
|
||||||
s.lastActiveAt = time(nullptr);
|
s.lastActiveAt = time(nullptr);
|
||||||
m_ScreenCtrlSessions[device_id] = std::move(s);
|
m_ScreenCtrlSessions[device_id] = std::move(s);
|
||||||
|
// 双向互斥(方向二):标记该设备屏幕子连接归 MCP 会话独占,人类 Web 观看期间被拒绝
|
||||||
|
// (HandleConnect 查 IsMcpTriggered)。会话擦除点(CloseScreenCtrlSession /
|
||||||
|
// SweepIdleScreenCtrl / OnScreenControlClosed / EndScreenCtrlAction)同步 ClearMcpTriggered。
|
||||||
|
WebService().SetMcpTriggered(device_id);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3881,6 +3885,7 @@ int CMcpServer::CloseScreenCtrlSession(uint64_t device_id, const std::string& se
|
|||||||
}
|
}
|
||||||
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
|
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
|
||||||
m_ScreenCtrlSessions.erase(it);
|
m_ScreenCtrlSessions.erase(it);
|
||||||
|
WebService().ClearMcpTriggered(device_id);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3893,8 +3898,10 @@ int CMcpServer::SweepIdleScreenCtrl(time_t idleTimeoutSec) {
|
|||||||
ScreenCtrlSession& s = it->second;
|
ScreenCtrlSession& s = it->second;
|
||||||
if (!s.busy && difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) {
|
if (!s.busy && difftime(now, s.lastActiveAt) > (double)idleTimeoutSec) {
|
||||||
if (s.subCtx) m_ScreenCtrlContextToDevice.erase(s.subCtx);
|
if (s.subCtx) m_ScreenCtrlContextToDevice.erase(s.subCtx);
|
||||||
toClose.push_back(it->first);
|
uint64_t devId = it->first;
|
||||||
|
toClose.push_back(devId);
|
||||||
it = m_ScreenCtrlSessions.erase(it);
|
it = m_ScreenCtrlSessions.erase(it);
|
||||||
|
WebService().ClearMcpTriggered(devId); // 锁内清除,避免与并发 remote_open 竞态
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
@@ -3921,8 +3928,10 @@ void CMcpServer::OnScreenControlClosed(context* subCtx) {
|
|||||||
// 注入在飞:不擦会话(注入线程仍持 subCtx),仅置 closed,由 EndScreenCtrlAction 收尾。
|
// 注入在飞:不擦会话(注入线程仍持 subCtx),仅置 closed,由 EndScreenCtrlAction 收尾。
|
||||||
sit->second.closed = true;
|
sit->second.closed = true;
|
||||||
} else {
|
} else {
|
||||||
|
uint64_t devId = it->second;
|
||||||
m_ScreenCtrlSessions.erase(sit);
|
m_ScreenCtrlSessions.erase(sit);
|
||||||
m_ScreenCtrlContextToDevice.erase(it);
|
m_ScreenCtrlContextToDevice.erase(it);
|
||||||
|
WebService().ClearMcpTriggered(devId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3954,6 +3963,7 @@ void CMcpServer::EndScreenCtrlAction(uint64_t device_id, const std::string& sess
|
|||||||
if (it->second.closed) { // 注入期间子连接已断:擦会话+路由
|
if (it->second.closed) { // 注入期间子连接已断:擦会话+路由
|
||||||
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
|
if (it->second.subCtx) m_ScreenCtrlContextToDevice.erase(it->second.subCtx);
|
||||||
m_ScreenCtrlSessions.erase(it);
|
m_ScreenCtrlSessions.erase(it);
|
||||||
|
WebService().ClearMcpTriggered(device_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -643,6 +643,15 @@ void CWebService::HandleConnect(void* ws_ptr, const std::string& token, uint64_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bidirectional mutex (direction 2, §8.2): reject human Web viewing while an MCP
|
||||||
|
// remote control session owns this host's screen sub-connection (SetMcpTriggered on
|
||||||
|
// remote_open, cleared on session teardown), so AI input cannot be injected into a
|
||||||
|
// picture a human is watching/controlling.
|
||||||
|
if (IsMcpTriggered(device_id)) {
|
||||||
|
SendText(ws_ptr, BuildJsonResponse("connect_result", false, "Device busy: remote control session active"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Check max clients per device
|
// Check max clients per device
|
||||||
int current_count = GetWebClientCount(device_id);
|
int current_count = GetWebClientCount(device_id);
|
||||||
if (current_count >= m_nMaxClientsPerDevice) {
|
if (current_count >= m_nMaxClientsPerDevice) {
|
||||||
@@ -2150,6 +2159,21 @@ void CWebService::ClearMfcTriggered(uint64_t device_id) {
|
|||||||
m_MfcTriggeredDevices.erase(device_id);
|
m_MfcTriggeredDevices.erase(device_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CWebService::SetMcpTriggered(uint64_t device_id) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_McpTriggeredMutex);
|
||||||
|
m_McpTriggeredDevices.insert(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CWebService::IsMcpTriggered(uint64_t device_id) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_McpTriggeredMutex);
|
||||||
|
return m_McpTriggeredDevices.find(device_id) != m_McpTriggeredDevices.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CWebService::ClearMcpTriggered(uint64_t device_id) {
|
||||||
|
std::lock_guard<std::mutex> lock(m_McpTriggeredMutex);
|
||||||
|
m_McpTriggeredDevices.erase(device_id);
|
||||||
|
}
|
||||||
|
|
||||||
bool CWebService::HasActiveSession(uint64_t device_id) {
|
bool CWebService::HasActiveSession(uint64_t device_id) {
|
||||||
std::lock_guard<std::mutex> lock(m_ScreenContextsMutex);
|
std::lock_guard<std::mutex> lock(m_ScreenContextsMutex);
|
||||||
return m_ScreenContexts.find(device_id) != m_ScreenContexts.end();
|
return m_ScreenContexts.find(device_id) != m_ScreenContexts.end();
|
||||||
|
|||||||
@@ -299,6 +299,12 @@ public:
|
|||||||
bool IsMfcTriggered(uint64_t device_id);
|
bool IsMfcTriggered(uint64_t device_id);
|
||||||
void ClearMfcTriggered(uint64_t device_id);
|
void ClearMfcTriggered(uint64_t device_id);
|
||||||
|
|
||||||
|
// MCP trigger management - an MCP remote control session owns the screen
|
||||||
|
// sub-connection and blocks human Web viewing (bidirectional mutex, §8.2).
|
||||||
|
void SetMcpTriggered(uint64_t device_id);
|
||||||
|
bool IsMcpTriggered(uint64_t device_id);
|
||||||
|
void ClearMcpTriggered(uint64_t device_id);
|
||||||
|
|
||||||
// Check if a remote desktop session already exists for device
|
// Check if a remote desktop session already exists for device
|
||||||
bool HasActiveSession(uint64_t device_id);
|
bool HasActiveSession(uint64_t device_id);
|
||||||
|
|
||||||
@@ -344,6 +350,10 @@ private:
|
|||||||
std::set<uint64_t> m_MfcTriggeredDevices;
|
std::set<uint64_t> m_MfcTriggeredDevices;
|
||||||
std::mutex m_MfcTriggeredMutex;
|
std::mutex m_MfcTriggeredMutex;
|
||||||
|
|
||||||
|
// MCP triggered devices: screen sub-connection owned by an MCP remote control session
|
||||||
|
std::set<uint64_t> m_McpTriggeredDevices;
|
||||||
|
std::mutex m_McpTriggeredMutex;
|
||||||
|
|
||||||
// Web 终端会话状态
|
// Web 终端会话状态
|
||||||
struct WebTermSession {
|
struct WebTermSession {
|
||||||
void* ws_ptr; // browser WebSocket
|
void* ws_ptr; // browser WebSocket
|
||||||
|
|||||||
Reference in New Issue
Block a user