feat: Add networking commands for z3ed CLI
- Implemented new CLI commands for network operations, including `net connect`, `net join`, `net leave`, and `net proposal` for real-time collaboration. - Enhanced the `CollaborationService` and `WebSocketClient` to support session management and proposal submissions. - Updated `README.md` to include usage instructions for the new networking commands and features. - Improved CMake configuration to include new source files for networking functionalities.
This commit is contained in:
@@ -889,16 +889,29 @@ The AI response appears in your chat history and can reference specific details
|
||||
|
||||
### ✅ Recently Completed (v0.2.0-alpha - October 5, 2025)
|
||||
|
||||
#### Core AI Features
|
||||
- **Enhanced System Prompt (v3)**: Proactive tool chaining with implicit iteration to minimize back-and-forth conversations
|
||||
- **Learn Command**: Full implementation with preferences, ROM patterns, project context, and conversation memory storage
|
||||
- **Native Gemini Function Calling**: Upgraded from manual curl to native function calling API with automatic tool schema generation
|
||||
- **Multimodal Vision Testing**: Comprehensive test suite for Gemini vision capabilities with screenshot integration
|
||||
- **AI-Controlled GUI Automation**: Natural language parsing (`AIActionParser`) and test script generation (`GuiActionGenerator`) for automated tile placement
|
||||
- **gRPC Windows Build Optimization**: vcpkg integration for 10-20x faster Windows builds, removed abseil-cpp submodule
|
||||
|
||||
#### Version Management & Protection
|
||||
- **ROM Version Management System**: `RomVersionManager` with automatic snapshots, safe points, corruption detection, and rollback capabilities
|
||||
- **Proposal Approval Framework**: `ProposalApprovalManager` with host/majority/unanimous voting modes to protect ROM from unwanted changes
|
||||
|
||||
#### Networking & Collaboration (NEW)
|
||||
- **Cross-Platform WebSocket Client**: `WebSocketClient` with Windows/macOS/Linux support using httplib
|
||||
- **Collaboration Service**: `CollaborationService` integrating version management with real-time networking
|
||||
- **yaze-server v2.0 Protocol**: Extended with proposal voting (`proposal_vote`, `proposal_vote_received`)
|
||||
- **z3ed Network Commands**: CLI commands for remote collaboration (`net connect`, `net join`, `proposal submit/wait`)
|
||||
- **Collaboration UI Panel**: `CollaborationPanel` widget with version history, ROM sync tracking, snapshot gallery, and approval workflow
|
||||
- **Improved Documentation**: Consolidated architecture, enhancement plans, and build instructions with JSON-first approach
|
||||
|
||||
#### Build System & Infrastructure
|
||||
- **gRPC Windows Build Optimization**: vcpkg integration for 10-20x faster Windows builds, removed abseil-cpp submodule
|
||||
- **Cross-Platform Networking**: Native socket support (ws2_32 on Windows, BSD sockets on Unix)
|
||||
- **Namespace Refactoring**: Created `app/net` namespace for networking components
|
||||
- **Improved Documentation**: Consolidated architecture, enhancement plans, networking guide, and build instructions with JSON-first approach
|
||||
|
||||
## 12. Troubleshooting
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ set(
|
||||
YAZE_NET_SRC
|
||||
app/net/rom_version_manager.cc
|
||||
app/net/websocket_client.cc
|
||||
app/net/collaboration_service.cc
|
||||
)
|
||||
|
||||
add_library(yaze_net STATIC ${YAZE_NET_SRC})
|
||||
|
||||
271
src/cli/handlers/net/net_commands.cc
Normal file
271
src/cli/handlers/net/net_commands.cc
Normal file
@@ -0,0 +1,271 @@
|
||||
#include "cli/handlers/net/net_commands.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "absl/time/time.h"
|
||||
|
||||
#ifdef YAZE_WITH_JSON
|
||||
#include "nlohmann/json.hpp"
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
// Global network client for CLI operations
|
||||
std::unique_ptr<Z3edNetworkClient> g_network_client;
|
||||
|
||||
void EnsureClient() {
|
||||
if (!g_network_client) {
|
||||
g_network_client = std::make_unique<Z3edNetworkClient>();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
absl::Status HandleNetConnect(const std::vector<std::string>& args) {
|
||||
EnsureClient();
|
||||
|
||||
std::string host = "localhost";
|
||||
int port = 8765;
|
||||
|
||||
// Parse arguments
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (args[i] == "--host" && i + 1 < args.size()) {
|
||||
host = args[i + 1];
|
||||
++i;
|
||||
} else if (args[i] == "--port" && i + 1 < args.size()) {
|
||||
port = std::stoi(args[i + 1]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Connecting to " << host << ":" << port << "..." << std::endl;
|
||||
|
||||
auto status = g_network_client->Connect(host, port);
|
||||
|
||||
if (status.ok()) {
|
||||
std::cout << "✓ Connected to yaze-server" << std::endl;
|
||||
} else {
|
||||
std::cerr << "✗ Connection failed: " << status.message() << std::endl;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
absl::Status HandleNetJoin(const std::vector<std::string>& args) {
|
||||
EnsureClient();
|
||||
|
||||
if (!g_network_client->IsConnected()) {
|
||||
return absl::FailedPreconditionError(
|
||||
"Not connected. Run: z3ed net connect");
|
||||
}
|
||||
|
||||
std::string session_code;
|
||||
std::string username;
|
||||
|
||||
// Parse arguments
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (args[i] == "--code" && i + 1 < args.size()) {
|
||||
session_code = args[i + 1];
|
||||
++i;
|
||||
} else if (args[i] == "--username" && i + 1 < args.size()) {
|
||||
username = args[i + 1];
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (session_code.empty() || username.empty()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: z3ed net join --code <CODE> --username <NAME>");
|
||||
}
|
||||
|
||||
std::cout << "Joining session " << session_code << " as " << username << "..."
|
||||
<< std::endl;
|
||||
|
||||
auto status = g_network_client->JoinSession(session_code, username);
|
||||
|
||||
if (status.ok()) {
|
||||
std::cout << "✓ Joined session successfully" << std::endl;
|
||||
} else {
|
||||
std::cerr << "✗ Failed to join: " << status.message() << std::endl;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
absl::Status HandleNetLeave(const std::vector<std::string>& args) {
|
||||
EnsureClient();
|
||||
|
||||
if (!g_network_client->IsConnected()) {
|
||||
return absl::FailedPreconditionError("Not connected");
|
||||
}
|
||||
|
||||
std::cout << "Leaving session..." << std::endl;
|
||||
|
||||
g_network_client->Disconnect();
|
||||
|
||||
std::cout << "✓ Left session" << std::endl;
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleNetProposal(const std::vector<std::string>& args) {
|
||||
EnsureClient();
|
||||
|
||||
if (!g_network_client->IsConnected()) {
|
||||
return absl::FailedPreconditionError(
|
||||
"Not connected. Run: z3ed net connect");
|
||||
}
|
||||
|
||||
if (args.empty()) {
|
||||
std::cout << "Usage:\n";
|
||||
std::cout << " z3ed net proposal submit --description <DESC> --data <JSON>\n";
|
||||
std::cout << " z3ed net proposal status --id <ID>\n";
|
||||
std::cout << " z3ed net proposal wait --id <ID> [--timeout <SEC>]\n";
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
std::string subcommand = args[0];
|
||||
std::vector<std::string> subargs(args.begin() + 1, args.end());
|
||||
|
||||
if (subcommand == "submit") {
|
||||
return HandleProposalSubmit(subargs);
|
||||
} else if (subcommand == "status") {
|
||||
return HandleProposalStatus(subargs);
|
||||
} else if (subcommand == "wait") {
|
||||
return HandleProposalWait(subargs);
|
||||
} else {
|
||||
return absl::InvalidArgumentError(
|
||||
absl::StrFormat("Unknown proposal subcommand: %s", subcommand));
|
||||
}
|
||||
}
|
||||
|
||||
absl::Status HandleProposalSubmit(const std::vector<std::string>& args) {
|
||||
std::string description;
|
||||
std::string data_json;
|
||||
std::string username = "cli_user"; // Default
|
||||
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (args[i] == "--description" && i + 1 < args.size()) {
|
||||
description = args[i + 1];
|
||||
++i;
|
||||
} else if (args[i] == "--data" && i + 1 < args.size()) {
|
||||
data_json = args[i + 1];
|
||||
++i;
|
||||
} else if (args[i] == "--username" && i + 1 < args.size()) {
|
||||
username = args[i + 1];
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (description.empty() || data_json.empty()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: z3ed net proposal submit --description <DESC> --data <JSON>");
|
||||
}
|
||||
|
||||
std::cout << "Submitting proposal..." << std::endl;
|
||||
std::cout << " Description: " << description << std::endl;
|
||||
|
||||
auto status = g_network_client->SubmitProposal(
|
||||
description,
|
||||
data_json,
|
||||
username
|
||||
);
|
||||
|
||||
if (status.ok()) {
|
||||
std::cout << "✓ Proposal submitted" << std::endl;
|
||||
std::cout << " Waiting for approval from host..." << std::endl;
|
||||
} else {
|
||||
std::cerr << "✗ Failed to submit: " << status.message() << std::endl;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
absl::Status HandleProposalStatus(const std::vector<std::string>& args) {
|
||||
std::string proposal_id;
|
||||
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (args[i] == "--id" && i + 1 < args.size()) {
|
||||
proposal_id = args[i + 1];
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (proposal_id.empty()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: z3ed net proposal status --id <ID>");
|
||||
}
|
||||
|
||||
auto status_result = g_network_client->GetProposalStatus(proposal_id);
|
||||
|
||||
if (status_result.ok()) {
|
||||
std::cout << "Proposal " << proposal_id.substr(0, 8) << "..." << std::endl;
|
||||
std::cout << " Status: " << *status_result << std::endl;
|
||||
} else {
|
||||
std::cerr << "✗ Failed to get status: " << status_result.status().message()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
return status_result.status();
|
||||
}
|
||||
|
||||
absl::Status HandleProposalWait(const std::vector<std::string>& args) {
|
||||
std::string proposal_id;
|
||||
int timeout_seconds = 60;
|
||||
|
||||
for (size_t i = 0; i < args.size(); ++i) {
|
||||
if (args[i] == "--id" && i + 1 < args.size()) {
|
||||
proposal_id = args[i + 1];
|
||||
++i;
|
||||
} else if (args[i] == "--timeout" && i + 1 < args.size()) {
|
||||
timeout_seconds = std::stoi(args[i + 1]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if (proposal_id.empty()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: z3ed net proposal wait --id <ID> [--timeout <SEC>]");
|
||||
}
|
||||
|
||||
std::cout << "Waiting for approval (timeout: " << timeout_seconds << "s)..."
|
||||
<< std::endl;
|
||||
|
||||
auto approved_result = g_network_client->WaitForApproval(
|
||||
proposal_id,
|
||||
timeout_seconds
|
||||
);
|
||||
|
||||
if (approved_result.ok()) {
|
||||
if (*approved_result) {
|
||||
std::cout << "✓ Proposal approved!" << std::endl;
|
||||
} else {
|
||||
std::cout << "✗ Proposal rejected" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cerr << "✗ Error: " << approved_result.status().message() << std::endl;
|
||||
}
|
||||
|
||||
return approved_result.status();
|
||||
}
|
||||
|
||||
absl::Status HandleNetStatus(const std::vector<std::string>& args) {
|
||||
EnsureClient();
|
||||
|
||||
std::cout << "Network Status:" << std::endl;
|
||||
std::cout << " Connected: "
|
||||
<< (g_network_client->IsConnected() ? "Yes" : "No") << std::endl;
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
48
src/cli/handlers/net/net_commands.h
Normal file
48
src/cli/handlers/net/net_commands.h
Normal file
@@ -0,0 +1,48 @@
|
||||
#ifndef YAZE_CLI_HANDLERS_NET_NET_COMMANDS_H_
|
||||
#define YAZE_CLI_HANDLERS_NET_NET_COMMANDS_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "cli/service/net/z3ed_network_client.h"
|
||||
|
||||
namespace yaze {
|
||||
namespace cli {
|
||||
namespace net {
|
||||
|
||||
/**
|
||||
* Handle 'z3ed net connect' command
|
||||
*/
|
||||
absl::Status HandleNetConnect(const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Handle 'z3ed net join' command
|
||||
*/
|
||||
absl::Status HandleNetJoin(const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Handle 'z3ed net leave' command
|
||||
*/
|
||||
absl::Status HandleNetLeave(const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Handle 'z3ed net proposal' command
|
||||
*/
|
||||
absl::Status HandleNetProposal(const std::vector<std::string>& args);
|
||||
|
||||
/**
|
||||
* Handle 'z3ed net status' command
|
||||
*/
|
||||
absl::Status HandleNetStatus(const std::vector<std::string>& args);
|
||||
|
||||
// Proposal subcommands
|
||||
absl::Status HandleProposalSubmit(const std::vector<std::string>& args);
|
||||
absl::Status HandleProposalStatus(const std::vector<std::string>& args);
|
||||
absl::Status HandleProposalWait(const std::vector<std::string>& args);
|
||||
|
||||
} // namespace net
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
#endif // YAZE_CLI_HANDLERS_NET_NET_COMMANDS_H_
|
||||
Reference in New Issue
Block a user