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:
scawful
2025-10-04 22:43:21 -04:00
parent 0f4d444a73
commit f19451e99e
4 changed files with 335 additions and 2 deletions

View File

@@ -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

View File

@@ -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})

View 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

View 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_