4#include <condition_variable>
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"
14#include "nlohmann/json.hpp"
15#define CPPHTTPLIB_OPENSSL_SUPPORT
34class Z3edNetworkClient::Impl {
36 Impl() : connected_(false), in_session_(false) {}
42 absl::Status
Connect(
const std::string& host,
int port) {
43 std::lock_guard<std::mutex> lock(mutex_);
46 return absl::AlreadyExistsError(
"Already connected");
54 client_ = std::make_unique<httplib::Client>(host, port);
55 client_->set_connection_timeout(5, 0);
56 client_->set_read_timeout(30, 0);
59 auto res = client_->Get(
"/health");
60 if (!res || res->status != 200) {
61 return absl::UnavailableError(
"Server not responding");
65 return absl::OkStatus();
67 }
catch (
const std::exception& e) {
68 return absl::UnavailableError(
69 absl::StrCat(
"Connection failed: ", e.what()));
74 std::lock_guard<std::mutex> lock(mutex_);
80 absl::Status
JoinSession(
const std::string& session_code,
81 const std::string& username) {
82 std::lock_guard<std::mutex> lock(mutex_);
85 return absl::FailedPreconditionError(
"Not connected");
89 nlohmann::json message = {
90 {
"type",
"join_session"},
92 {
"session_code", session_code},
93 {
"username", username}
97 auto res = client_->Post(
"/message", message.dump(),
"application/json");
99 if (!res || res->status != 200) {
100 return absl::InternalError(
"Failed to join session");
104 session_code_ = session_code;
105 username_ = username;
107 return absl::OkStatus();
109 }
catch (
const std::exception& e) {
110 return absl::InternalError(absl::StrCat(
"Join failed: ", e.what()));
115 const std::string& proposal_json,
116 const std::string& username) {
117 std::lock_guard<std::mutex> lock(mutex_);
119 if (!connected_ || !in_session_) {
120 return absl::FailedPreconditionError(
"Not in a session");
124 nlohmann::json proposal_data = nlohmann::json::parse(proposal_json);
125 proposal_data[
"description"] = description;
127 nlohmann::json message = {
128 {
"type",
"proposal_share"},
130 {
"sender", username},
131 {
"proposal_data", proposal_data}
135 auto res = client_->Post(
"/message", message.dump(),
"application/json");
137 if (!res || res->status != 200) {
138 return absl::InternalError(
"Failed to submit proposal");
142 if (!res->body.empty()) {
144 auto response_json = nlohmann::json::parse(res->body);
145 if (response_json.contains(
"proposal_id")) {
146 last_proposal_id_ = response_json[
"proposal_id"];
153 return absl::OkStatus();
155 }
catch (
const std::exception& e) {
156 return absl::InternalError(
157 absl::StrCat(
"Proposal submission failed: ", e.what()));
162 std::lock_guard<std::mutex> lock(mutex_);
165 return absl::FailedPreconditionError(
"Not connected");
170 auto res = client_->Get(
171 absl::StrFormat(
"/proposal/%s/status", proposal_id).c_str());
173 if (!res || res->status != 200) {
174 return absl::NotFoundError(
"Proposal not found");
177 auto response = nlohmann::json::parse(res->body);
178 return response[
"status"].get<std::string>();
180 }
catch (
const std::exception& e) {
181 return absl::InternalError(
182 absl::StrCat(
"Status check failed: ", e.what()));
187 int timeout_seconds) {
188 auto deadline = absl::Now() + absl::Seconds(timeout_seconds);
190 while (absl::Now() < deadline) {
193 if (!status_result.ok()) {
194 return status_result.status();
197 std::string status = *status_result;
199 if (status ==
"approved" || status ==
"applied") {
201 }
else if (status ==
"rejected") {
206 absl::SleepFor(absl::Seconds(1));
209 return absl::DeadlineExceededError(
"Approval timeout");
212 absl::Status
SendMessage(
const std::string& message,
213 const std::string& sender) {
214 std::lock_guard<std::mutex> lock(mutex_);
216 if (!connected_ || !in_session_) {
217 return absl::FailedPreconditionError(
"Not in a session");
221 nlohmann::json msg = {
222 {
"type",
"chat_message"},
224 {
"message", message},
229 auto res = client_->Post(
"/message", msg.dump(),
"application/json");
231 if (!res || res->status != 200) {
232 return absl::InternalError(
"Failed to send message");
235 return absl::OkStatus();
237 }
catch (
const std::exception& e) {
238 return absl::InternalError(absl::StrCat(
"Send failed: ", e.what()));
242 absl::StatusOr<std::string>
QueryAI(
const std::string& query,
243 const std::string& username) {
244 std::lock_guard<std::mutex> lock(mutex_);
246 if (!connected_ || !in_session_) {
247 return absl::FailedPreconditionError(
"Not in a session");
251 nlohmann::json message = {
252 {
"type",
"ai_query"},
255 {
"username", username}
259 auto res = client_->Post(
"/message", message.dump(),
"application/json");
261 if (!res || res->status != 200) {
262 return absl::InternalError(
"AI query failed");
267 return std::string(
"AI agent endpoint not configured");
269 }
catch (
const std::exception& e) {
270 return absl::InternalError(absl::StrCat(
"AI query failed: ", e.what()));
275 std::lock_guard<std::mutex> lock(mutex_);
280 std::lock_guard<std::mutex> lock(mutex_);
281 return last_proposal_id_;
285 mutable std::mutex mutex_;
286 std::unique_ptr<httplib::Client> client_;
293 std::string session_code_;
294 std::string username_;
295 std::string last_proposal_id_;
303 absl::Status
Connect(
const std::string&,
int) {
304 return absl::UnimplementedError(
"Network support requires JSON library");
307 absl::Status
JoinSession(
const std::string&,
const std::string&) {
308 return absl::UnimplementedError(
"Network support requires JSON library");
310 absl::Status
SubmitProposal(
const std::string&,
const std::string&,
const std::string&) {
311 return absl::UnimplementedError(
"Network support requires JSON library");
314 return absl::UnimplementedError(
"Network support requires JSON library");
317 return absl::UnimplementedError(
"Network support requires JSON library");
319 absl::Status
SendMessage(
const std::string&,
const std::string&) {
320 return absl::UnimplementedError(
"Network support requires JSON library");
322 absl::StatusOr<std::string>
QueryAI(
const std::string&,
const std::string&) {
323 return absl::UnimplementedError(
"Network support requires JSON library");
336 : impl_(std::make_unique<
Impl>()) {
342 return impl_->Connect(host, port);
346 const std::string& session_code,
347 const std::string& username) {
348 return impl_->JoinSession(session_code, username);
352 const std::string& description,
353 const std::string& proposal_json,
354 const std::string& username) {
355 return impl_->SubmitProposal(description, proposal_json, username);
359 const std::string& proposal_id) {
360 return impl_->GetProposalStatus(proposal_id);
364 const std::string& proposal_id,
365 int timeout_seconds) {
366 return impl_->WaitForApproval(proposal_id, timeout_seconds);
370 const std::string& message,
371 const std::string& sender) {
372 return impl_->SendMessage(message, sender);
376 const std::string& query,
377 const std::string& username) {
378 return impl_->QueryAI(query, username);
386 return impl_->IsConnected();
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 &)
std::string GetLastProposalId() const
absl::StatusOr< bool > WaitForApproval(const std::string &proposal_id, int timeout_seconds=60)
absl::Status SendMessage(const std::string &message, const std::string &sender)
std::unique_ptr< Impl > impl_
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.