yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
z3ed_network_client.cc
Go to the documentation of this file.
2
3#include <chrono>
4#include <condition_variable>
5#include <mutex>
6#include <thread>
7
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_format.h"
10#include "absl/time/clock.h"
11#include "absl/time/time.h"
12
13#ifdef YAZE_WITH_JSON
14#include "nlohmann/json.hpp"
15#define CPPHTTPLIB_OPENSSL_SUPPORT
16#include "httplib.h"
17#endif
18
19// Undefine Windows macros that conflict with our method names
20// Must be outside the #ifdef so it applies to all code below
21#ifdef _WIN32
22#ifdef SendMessage
23#undef SendMessage
24#endif
25#endif
26
27namespace yaze {
28namespace cli {
29namespace net {
30
31#ifdef YAZE_WITH_JSON
32
33// Implementation using httplib for cross-platform WebSocket support
34class Z3edNetworkClient::Impl {
35 public:
36 Impl() : connected_(false), in_session_(false) {}
37
38 ~Impl() {
39 Disconnect();
40 }
41
42 absl::Status Connect(const std::string& host, int port) {
43 std::lock_guard<std::mutex> lock(mutex_);
44
45 if (connected_) {
46 return absl::AlreadyExistsError("Already connected");
47 }
48
49 host_ = host;
50 port_ = port;
51
52 try {
53 // Create HTTP client for WebSocket fallback
54 client_ = std::make_unique<httplib::Client>(host, port);
55 client_->set_connection_timeout(5, 0);
56 client_->set_read_timeout(30, 0);
57
58 // Test connection
59 auto res = client_->Get("/health");
60 if (!res || res->status != 200) {
61 return absl::UnavailableError("Server not responding");
62 }
63
64 connected_ = true;
65 return absl::OkStatus();
66
67 } catch (const std::exception& e) {
68 return absl::UnavailableError(
69 absl::StrCat("Connection failed: ", e.what()));
70 }
71 }
72
73 void Disconnect() {
74 std::lock_guard<std::mutex> lock(mutex_);
75 connected_ = false;
76 in_session_ = false;
77 client_.reset();
78 }
79
80 absl::Status JoinSession(const std::string& session_code,
81 const std::string& username) {
82 std::lock_guard<std::mutex> lock(mutex_);
83
84 if (!connected_) {
85 return absl::FailedPreconditionError("Not connected");
86 }
87
88 try {
89 nlohmann::json message = {
90 {"type", "join_session"},
91 {"payload", {
92 {"session_code", session_code},
93 {"username", username}
94 }}
95 };
96
97 auto res = client_->Post("/message", message.dump(), "application/json");
98
99 if (!res || res->status != 200) {
100 return absl::InternalError("Failed to join session");
101 }
102
103 in_session_ = true;
104 session_code_ = session_code;
105 username_ = username;
106
107 return absl::OkStatus();
108
109 } catch (const std::exception& e) {
110 return absl::InternalError(absl::StrCat("Join failed: ", e.what()));
111 }
112 }
113
114 absl::Status SubmitProposal(const std::string& description,
115 const std::string& proposal_json,
116 const std::string& username) {
117 std::lock_guard<std::mutex> lock(mutex_);
118
119 if (!connected_ || !in_session_) {
120 return absl::FailedPreconditionError("Not in a session");
121 }
122
123 try {
124 nlohmann::json proposal_data = nlohmann::json::parse(proposal_json);
125 proposal_data["description"] = description;
126
127 nlohmann::json message = {
128 {"type", "proposal_share"},
129 {"payload", {
130 {"sender", username},
131 {"proposal_data", proposal_data}
132 }}
133 };
134
135 auto res = client_->Post("/message", message.dump(), "application/json");
136
137 if (!res || res->status != 200) {
138 return absl::InternalError("Failed to submit proposal");
139 }
140
141 // Extract proposal ID from response if available
142 if (!res->body.empty()) {
143 try {
144 auto response_json = nlohmann::json::parse(res->body);
145 if (response_json.contains("proposal_id")) {
146 last_proposal_id_ = response_json["proposal_id"];
147 }
148 } catch (...) {
149 // Response parsing failed, continue
150 }
151 }
152
153 return absl::OkStatus();
154
155 } catch (const std::exception& e) {
156 return absl::InternalError(
157 absl::StrCat("Proposal submission failed: ", e.what()));
158 }
159 }
160
161 absl::StatusOr<std::string> GetProposalStatus(const std::string& proposal_id) {
162 std::lock_guard<std::mutex> lock(mutex_);
163
164 if (!connected_) {
165 return absl::FailedPreconditionError("Not connected");
166 }
167
168 try {
169 // Query server for proposal status
170 auto res = client_->Get(
171 absl::StrFormat("/proposal/%s/status", proposal_id).c_str());
172
173 if (!res || res->status != 200) {
174 return absl::NotFoundError("Proposal not found");
175 }
176
177 auto response = nlohmann::json::parse(res->body);
178 return response["status"].get<std::string>();
179
180 } catch (const std::exception& e) {
181 return absl::InternalError(
182 absl::StrCat("Status check failed: ", e.what()));
183 }
184 }
185
186 absl::StatusOr<bool> WaitForApproval(const std::string& proposal_id,
187 int timeout_seconds) {
188 auto deadline = absl::Now() + absl::Seconds(timeout_seconds);
189
190 while (absl::Now() < deadline) {
191 auto status_result = GetProposalStatus(proposal_id);
192
193 if (!status_result.ok()) {
194 return status_result.status();
195 }
196
197 std::string status = *status_result;
198
199 if (status == "approved" || status == "applied") {
200 return true;
201 } else if (status == "rejected") {
202 return false;
203 }
204
205 // Poll every second
206 absl::SleepFor(absl::Seconds(1));
207 }
208
209 return absl::DeadlineExceededError("Approval timeout");
210 }
211
212 absl::Status SendMessage(const std::string& message,
213 const std::string& sender) {
214 std::lock_guard<std::mutex> lock(mutex_);
215
216 if (!connected_ || !in_session_) {
217 return absl::FailedPreconditionError("Not in a session");
218 }
219
220 try {
221 nlohmann::json msg = {
222 {"type", "chat_message"},
223 {"payload", {
224 {"message", message},
225 {"sender", sender}
226 }}
227 };
228
229 auto res = client_->Post("/message", msg.dump(), "application/json");
230
231 if (!res || res->status != 200) {
232 return absl::InternalError("Failed to send message");
233 }
234
235 return absl::OkStatus();
236
237 } catch (const std::exception& e) {
238 return absl::InternalError(absl::StrCat("Send failed: ", e.what()));
239 }
240 }
241
242 absl::StatusOr<std::string> QueryAI(const std::string& query,
243 const std::string& username) {
244 std::lock_guard<std::mutex> lock(mutex_);
245
246 if (!connected_ || !in_session_) {
247 return absl::FailedPreconditionError("Not in a session");
248 }
249
250 try {
251 nlohmann::json message = {
252 {"type", "ai_query"},
253 {"payload", {
254 {"query", query},
255 {"username", username}
256 }}
257 };
258
259 auto res = client_->Post("/message", message.dump(), "application/json");
260
261 if (!res || res->status != 200) {
262 return absl::InternalError("AI query failed");
263 }
264
265 // Wait for response (in a real implementation, this would use callbacks)
266 // For now, return placeholder
267 return std::string("AI agent endpoint not configured");
268
269 } catch (const std::exception& e) {
270 return absl::InternalError(absl::StrCat("AI query failed: ", e.what()));
271 }
272 }
273
274 bool IsConnected() const {
275 std::lock_guard<std::mutex> lock(mutex_);
276 return connected_;
277 }
278
279 std::string GetLastProposalId() const {
280 std::lock_guard<std::mutex> lock(mutex_);
281 return last_proposal_id_;
282 }
283
284 private:
285 mutable std::mutex mutex_;
286 std::unique_ptr<httplib::Client> client_;
287
288 std::string host_;
289 int port_;
290 bool connected_;
291 bool in_session_;
292
293 std::string session_code_;
294 std::string username_;
295 std::string last_proposal_id_;
296};
297
298#else
299
300// Stub implementation when JSON is not available
302 public:
303 absl::Status Connect(const std::string&, int) {
304 return absl::UnimplementedError("Network support requires JSON library");
305 }
306 void Disconnect() {}
307 absl::Status JoinSession(const std::string&, const std::string&) {
308 return absl::UnimplementedError("Network support requires JSON library");
309 }
310 absl::Status SubmitProposal(const std::string&, const std::string&, const std::string&) {
311 return absl::UnimplementedError("Network support requires JSON library");
312 }
313 absl::StatusOr<std::string> GetProposalStatus(const std::string&) {
314 return absl::UnimplementedError("Network support requires JSON library");
315 }
316 absl::StatusOr<bool> WaitForApproval(const std::string&, int) {
317 return absl::UnimplementedError("Network support requires JSON library");
318 }
319 absl::Status SendMessage(const std::string&, const std::string&) {
320 return absl::UnimplementedError("Network support requires JSON library");
321 }
322 absl::StatusOr<std::string> QueryAI(const std::string&, const std::string&) {
323 return absl::UnimplementedError("Network support requires JSON library");
324 }
325 bool IsConnected() const { return false; }
326 std::string GetLastProposalId() const { return ""; }
327};
328
329#endif // YAZE_WITH_JSON
330
331// ============================================================================
332// Z3edNetworkClient Implementation
333// ============================================================================
334
336 : impl_(std::make_unique<Impl>()) {
337}
338
340
341absl::Status Z3edNetworkClient::Connect(const std::string& host, int port) {
342 return impl_->Connect(host, port);
343}
344
346 const std::string& session_code,
347 const std::string& username) {
348 return impl_->JoinSession(session_code, username);
349}
350
352 const std::string& description,
353 const std::string& proposal_json,
354 const std::string& username) {
355 return impl_->SubmitProposal(description, proposal_json, username);
356}
357
358absl::StatusOr<std::string> Z3edNetworkClient::GetProposalStatus(
359 const std::string& proposal_id) {
360 return impl_->GetProposalStatus(proposal_id);
361}
362
364 const std::string& proposal_id,
365 int timeout_seconds) {
366 return impl_->WaitForApproval(proposal_id, timeout_seconds);
367}
368
370 const std::string& message,
371 const std::string& sender) {
372 return impl_->SendMessage(message, sender);
373}
374
375absl::StatusOr<std::string> Z3edNetworkClient::QueryAI(
376 const std::string& query,
377 const std::string& username) {
378 return impl_->QueryAI(query, username);
379}
380
382 impl_->Disconnect();
383}
384
386 return impl_->IsConnected();
387}
388
389} // namespace net
390} // namespace cli
391} // namespace yaze
absl::Status SendMessage(const std::string &, const std::string &)
absl::Status Connect(const std::string &, int)
absl::StatusOr< bool > WaitForApproval(const std::string &, int)
absl::Status JoinSession(const std::string &, const std::string &)
absl::StatusOr< std::string > GetProposalStatus(const std::string &)
absl::Status SubmitProposal(const std::string &, const std::string &, const std::string &)
absl::StatusOr< std::string > QueryAI(const std::string &, const std::string &)
absl::StatusOr< bool > WaitForApproval(const std::string &proposal_id, int timeout_seconds=60)
absl::Status SendMessage(const std::string &message, const std::string &sender)
absl::Status JoinSession(const std::string &session_code, const std::string &username)
absl::StatusOr< std::string > QueryAI(const std::string &query, const std::string &username)
absl::Status Connect(const std::string &host, int port=8765)
absl::StatusOr< std::string > GetProposalStatus(const std::string &proposal_id)
absl::Status SubmitProposal(const std::string &description, const std::string &proposal_json, const std::string &username)
Main namespace for the application.
Definition controller.cc:20