[feat] improve CLI commands, controls and npm packaging

This commit is contained in:
dijunkun
2026-10-30 13:32:34 +08:00
parent 5d0a8f857f
commit e6e7e4c0f0
19 changed files with 359 additions and 102 deletions
@@ -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
+10 -3
View File
@@ -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;