mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-11-03 04:22:43 +08:00
[feat] improve CLI commands, controls and npm packaging
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2026-10-30
|
||||
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _CONTROLLER_DIAGNOSTICS_H_
|
||||
#define _CONTROLLER_DIAGNOSTICS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace crossdesk {
|
||||
void InitializeControllerDiagnostics(const std::string &directory);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "controller_diagnostics.h"
|
||||
#include <log.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
// Signaling errors and SDP can contain credentials. Only audited transport
|
||||
// events reach this sink; arbitrary MiniRTC messages remain suppressed.
|
||||
std::string DiagnosticEvent(const spdlog::details::log_msg &message) {
|
||||
std::string_view source = message.source.filename ? message.source.filename : "";
|
||||
const auto slash = source.find_last_of("/\\");
|
||||
if (slash != std::string_view::npos)
|
||||
source.remove_prefix(slash + 1);
|
||||
const std::string_view text(message.payload.data(), message.payload.size());
|
||||
if (source == "controller_session.cpp")
|
||||
return std::string(text); // Local states/counters only at these call sites.
|
||||
if (source == "ice_transport.cpp") {
|
||||
const auto marker = text.rfind("state_change: ");
|
||||
if (marker != std::string_view::npos) {
|
||||
const auto state = text.substr(marker + 14);
|
||||
for (const auto allowed : {"disconnected", "gathering", "connecting",
|
||||
"connected", "ready", "failed"})
|
||||
if (state == allowed)
|
||||
return "ICE state: " + std::string(state);
|
||||
}
|
||||
for (const auto allowed : {"Traversal using relay server", "Traversal using p2p",
|
||||
"Selected ICE pair is not available yet"})
|
||||
if (text == allowed)
|
||||
return std::string(text);
|
||||
}
|
||||
if (source == "ice_agent.cpp") {
|
||||
for (const auto allowed : {"DTLS handshake started, awaiting peer packets...",
|
||||
"DTLS handshake retransmission failed",
|
||||
"DTLS peer certificate missing",
|
||||
"DTLS peer fingerprint mismatch",
|
||||
"DTLS peer fingerprint verified",
|
||||
"DTLS peer not verified", "No SRTP profile selected",
|
||||
"SSL_export_keying_material failed", "StartDtls failed",
|
||||
"libnice has no relay upgrade extension; rebuild dependencies"})
|
||||
if (text == allowed)
|
||||
return std::string(text);
|
||||
for (const auto prefix : {"DTLS handshake timed out after ",
|
||||
"DTLS handshake start failed, err=",
|
||||
"DTLS packet processing failed, err="})
|
||||
if (text.substr(0, std::char_traits<char>::length(prefix)) == prefix)
|
||||
return std::string(prefix); // Omit variable payload entirely.
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
class DiagnosticSink final : public spdlog::sinks::base_sink<std::mutex> {
|
||||
public:
|
||||
explicit DiagnosticSink(const std::string &directory)
|
||||
: file_(directory + "/transport.log", 1024 * 1024, 2) {}
|
||||
|
||||
private:
|
||||
void sink_it_(const spdlog::details::log_msg &message) override {
|
||||
const auto event = DiagnosticEvent(message);
|
||||
if (event.empty())
|
||||
return;
|
||||
auto sanitized = message;
|
||||
sanitized.payload = spdlog::string_view_t(event.data(), event.size());
|
||||
file_.log(sanitized);
|
||||
file_.flush();
|
||||
}
|
||||
void flush_() override { file_.flush(); }
|
||||
spdlog::sinks::rotating_file_sink_mt file_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void InitializeControllerDiagnostics(const std::string &directory) {
|
||||
auto sink = std::make_shared<DiagnosticSink>(directory);
|
||||
minirtc::InitLogger(directory);
|
||||
auto logger = minirtc::get_logger();
|
||||
// Connect calls this after the previous peer and its callbacks stop.
|
||||
logger->sinks() = {std::move(sink)};
|
||||
logger->set_level(spdlog::level::info);
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "controller_session.h"
|
||||
#include "controller_diagnostics.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
@@ -136,8 +137,8 @@ void ControllerSession::Connect(SessionOptions options, std::string device,
|
||||
// The server assigns a temporary controller identity on this connection.
|
||||
// Never register a desktop identity or persist local credentials.
|
||||
client_id_.clear();
|
||||
minirtc::InitLogger(options_.log_directory);
|
||||
minirtc::get_logger()->set_level(spdlog::level::off);
|
||||
InitializeControllerDiagnostics(options_.log_directory);
|
||||
SPDLOG_LOGGER_INFO(minirtc::get_logger(), "Session registering");
|
||||
auto p = Parameters();
|
||||
peer_ = CreatePeer(&p);
|
||||
if (!peer_) {
|
||||
@@ -169,6 +170,7 @@ void ControllerSession::Tick() {
|
||||
if (Finished())
|
||||
break;
|
||||
if (e.kind == 0) {
|
||||
SPDLOG_LOGGER_INFO(minirtc::get_logger(), "Signal status: {}", e.code);
|
||||
if (e.code == SignalTlsCertError) {
|
||||
Fail("tls_certificate_error");
|
||||
break;
|
||||
@@ -199,6 +201,8 @@ void ControllerSession::Tick() {
|
||||
password_.clear();
|
||||
}
|
||||
} else if (e.kind == 1) {
|
||||
SPDLOG_LOGGER_INFO(minirtc::get_logger(), "Connection status: {} (session {})",
|
||||
e.code, state_);
|
||||
if (e.code == ::Connected && !Connected()) {
|
||||
state_ = "connected";
|
||||
auto hello = MakeHostInformation("CrossDesk CLI", {}, false);
|
||||
@@ -215,7 +219,7 @@ void ControllerSession::Tick() {
|
||||
else if (e.code == RemoteUnavailable)
|
||||
Fail("device_offline_or_unavailable");
|
||||
else if (e.code == Failed)
|
||||
Fail("connection_failed");
|
||||
Fail(Connected() ? "connection_lost" : "connection_failed");
|
||||
else if (e.code == Closed || e.code == Disconnected)
|
||||
Fail("remote_disconnected");
|
||||
} else if (e.kind == 2 && Connected()) {
|
||||
@@ -275,6 +279,7 @@ void ControllerSession::ClosePeers() {
|
||||
password_.clear();
|
||||
}
|
||||
void ControllerSession::Fail(const char *reason) {
|
||||
SPDLOG_LOGGER_INFO(minirtc::get_logger(), "Session failed: {}", reason);
|
||||
ReleaseInput();
|
||||
error_ = reason;
|
||||
state_ = "failed";
|
||||
@@ -345,6 +350,8 @@ void ControllerSession::Preview() {
|
||||
Send(kControlStream, s.ToJson().dump());
|
||||
}
|
||||
void ControllerSession::SetControl(bool enabled) {
|
||||
if (control_ != enabled)
|
||||
SPDLOG_LOGGER_INFO(minirtc::get_logger(), "Remote control: {}", enabled);
|
||||
if (!enabled)
|
||||
ReleaseInput();
|
||||
control_ = enabled;
|
||||
|
||||
@@ -54,7 +54,7 @@ bool KittyReply(const std::string &response);
|
||||
// Incremental parser: protocol replies and unknown escapes are consumed
|
||||
// locally. Key taps always release immediately; paste uses the UTF-8 text
|
||||
// protocol. In command_mode, control records the mode to restore while the
|
||||
// session's remote input is suspended.
|
||||
// session's remote input is suspended until the user explicitly leaves the mode.
|
||||
class Input {
|
||||
public:
|
||||
void Feed(const std::string &bytes, ControllerSession &, const Grid *grid);
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
std::string error;
|
||||
double zoom = 1, center_x = .5, center_y = .5;
|
||||
uint64_t view_revision = 0;
|
||||
uint64_t mouse_reports = 0;
|
||||
|
||||
private:
|
||||
std::string pending_;
|
||||
|
||||
+15
-14
@@ -31,10 +31,14 @@ bool Input::LocalKey(int key, ControllerSession &s, const Grid *grid) {
|
||||
s.SetControl(false);
|
||||
return true;
|
||||
}
|
||||
bool command = command_mode;
|
||||
command_mode = false;
|
||||
if (key == 'q' || (key == 3 && !command)) {
|
||||
if (command_mode && (key == 27 || key == 3)) {
|
||||
command_mode = false;
|
||||
s.SetControl(control);
|
||||
return true;
|
||||
}
|
||||
if (key == 'q' || key == 3) {
|
||||
exit = true;
|
||||
command_mode = false;
|
||||
control = false;
|
||||
} else if (key == 'c') {
|
||||
control = !control;
|
||||
@@ -42,12 +46,12 @@ bool Input::LocalKey(int key, ControllerSession &s, const Grid *grid) {
|
||||
key == 'd' || key == 'i' || key == 'j' || key == 'k' ||
|
||||
key == 'l') {
|
||||
Navigate(char(key), grid);
|
||||
} else if (!command) {
|
||||
} else if (!command_mode) {
|
||||
return false;
|
||||
} else if (key != 27 && key != 3) {
|
||||
error = "Unknown local command; Ctrl+] for help";
|
||||
} else {
|
||||
error = "Unknown local command; ? for help";
|
||||
}
|
||||
s.SetControl(control);
|
||||
s.SetControl(control && !command_mode);
|
||||
return true;
|
||||
}
|
||||
void Input::Navigate(char key, const Grid *grid) {
|
||||
@@ -94,7 +98,6 @@ void Input::Feed(const std::string &bytes, ControllerSession &s,
|
||||
if (pending_.size() + bytes.size() > 32768) {
|
||||
pending_.clear();
|
||||
pasting_ = false;
|
||||
command_mode = false;
|
||||
s.SetControl(false);
|
||||
control = false;
|
||||
return;
|
||||
@@ -117,11 +120,8 @@ void Input::Feed(const std::string &bytes, ControllerSession &s,
|
||||
auto end = pending_.find("\033[201~");
|
||||
if (end == std::string::npos)
|
||||
return; // bounded by 32 KiB; one UTF-8 transaction
|
||||
if (command_mode) {
|
||||
// Pasted text must never become local commands or leak to the remote.
|
||||
command_mode = false;
|
||||
s.SetControl(control);
|
||||
} else if (control && !s.Text(pending_.substr(0, end)))
|
||||
// Drop local-mode paste without leaving the mode or running commands.
|
||||
if (control && !command_mode && !s.Text(pending_.substr(0, end)))
|
||||
error = "Remote UTF-8 text input unavailable";
|
||||
pending_.erase(0, end + 6);
|
||||
pasting_ = false;
|
||||
@@ -159,7 +159,6 @@ void Input::Feed(const std::string &bytes, ControllerSession &s,
|
||||
pending_.erase(0, end + 1);
|
||||
if (seq == "\033[O") {
|
||||
control = false;
|
||||
command_mode = false;
|
||||
s.SetControl(false);
|
||||
continue;
|
||||
}
|
||||
@@ -224,6 +223,8 @@ void Input::Feed(const std::string &bytes, ControllerSession &s,
|
||||
} else
|
||||
mouse = mouse && cur == limit;
|
||||
}
|
||||
if (mouse)
|
||||
++mouse_reports;
|
||||
if (grid && mouse && !command_mode) {
|
||||
auto pos = MapPointer(*grid, x - 1, y - 1);
|
||||
if (!control) {
|
||||
|
||||
Reference in New Issue
Block a user