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) {}
40 absl::Status
Connect(
const std::string& host,
int port) {
41 std::lock_guard<std::mutex> lock(mutex_);
44 return absl::AlreadyExistsError(
"Already connected");
52 client_ = std::make_unique<httplib::Client>(host, port);
53 client_->set_connection_timeout(5, 0);
54 client_->set_read_timeout(30, 0);
57 auto res = client_->Get(
"/health");
58 if (!res || res->status != 200) {
59 return absl::UnavailableError(
"Server not responding");
63 return absl::OkStatus();
65 }
catch (
const std::exception& e) {
66 return absl::UnavailableError(
67 absl::StrCat(
"Connection failed: ", e.what()));
72 std::lock_guard<std::mutex> lock(mutex_);
78 absl::Status
JoinSession(
const std::string& session_code,
79 const std::string& username) {
80 std::lock_guard<std::mutex> lock(mutex_);
83 return absl::FailedPreconditionError(
"Not connected");
87 nlohmann::json message = {
88 {
"type",
"join_session"},
90 {{
"session_code", session_code}, {
"username", username}}}};
92 auto res = client_->Post(
"/message", message.dump(),
"application/json");
94 if (!res || res->status != 200) {
95 return absl::InternalError(
"Failed to join session");
99 session_code_ = session_code;
100 username_ = username;
102 return absl::OkStatus();
104 }
catch (
const std::exception& e) {
105 return absl::InternalError(absl::StrCat(
"Join failed: ", e.what()));
110 const std::string& proposal_json,
111 const std::string& username) {
112 std::lock_guard<std::mutex> lock(mutex_);
114 if (!connected_ || !in_session_) {
115 return absl::FailedPreconditionError(
"Not in a session");
119 nlohmann::json proposal_data = nlohmann::json::parse(proposal_json);
122 nlohmann::json message = {
123 {
"type",
"proposal_share"},
125 {{
"sender", username}, {
"proposal_data", proposal_data}}}};
127 auto res = client_->Post(
"/message", message.dump(),
"application/json");
129 if (!res || res->status != 200) {
130 return absl::InternalError(
"Failed to submit proposal");
134 if (!res->body.empty()) {
136 auto response_json = nlohmann::json::parse(res->body);
137 if (response_json.contains(
"proposal_id")) {
138 last_proposal_id_ = response_json[
"proposal_id"];
145 return absl::OkStatus();
147 }
catch (
const std::exception& e) {
148 return absl::InternalError(
149 absl::StrCat(
"Proposal submission failed: ", e.what()));
154 const std::string& proposal_id) {
155 std::lock_guard<std::mutex> lock(mutex_);
158 return absl::FailedPreconditionError(
"Not connected");
163 auto res = client_->Get(
164 absl::StrFormat(
"/proposal/%s/status", proposal_id).c_str());
166 if (!res || res->status != 200) {
167 return absl::NotFoundError(
"Proposal not found");
170 auto response = nlohmann::json::parse(res->body);
171 return response[
"status"].get<std::string>();
173 }
catch (
const std::exception& e) {
174 return absl::InternalError(
175 absl::StrCat(
"Status check failed: ", e.what()));
180 int timeout_seconds) {
181 auto deadline = absl::Now() + absl::Seconds(timeout_seconds);
183 while (absl::Now() < deadline) {
186 if (!status_result.ok()) {
187 return status_result.status();
190 std::string status = *status_result;
192 if (status ==
"approved" || status ==
"applied") {
194 }
else if (status ==
"rejected") {
199 absl::SleepFor(absl::Seconds(1));
202 return absl::DeadlineExceededError(
"Approval timeout");
205 absl::Status
SendMessage(
const std::string& message,
206 const std::string& sender) {
207 std::lock_guard<std::mutex> lock(mutex_);
209 if (!connected_ || !in_session_) {
210 return absl::FailedPreconditionError(
"Not in a session");
214 nlohmann::json msg = {
215 {
"type",
"chat_message"},
216 {
"payload", {{
"message", message}, {
"sender", sender}}}};
218 auto res = client_->Post(
"/message", msg.dump(),
"application/json");
220 if (!res || res->status != 200) {
221 return absl::InternalError(
"Failed to send message");
224 return absl::OkStatus();
226 }
catch (
const std::exception& e) {
227 return absl::InternalError(absl::StrCat(
"Send failed: ", e.what()));
231 absl::StatusOr<std::string>
QueryAI(
const std::string& query,
232 const std::string& username) {
233 std::lock_guard<std::mutex> lock(mutex_);
235 if (!connected_ || !in_session_) {
236 return absl::FailedPreconditionError(
"Not in a session");
240 nlohmann::json message = {
241 {
"type",
"ai_query"},
242 {
"payload", {{
"query", query}, {
"username", username}}}};
244 auto res = client_->Post(
"/message", message.dump(),
"application/json");
246 if (!res || res->status != 200) {
247 return absl::InternalError(
"AI query failed");
252 return std::string(
"AI agent endpoint not configured");
254 }
catch (
const std::exception& e) {
255 return absl::InternalError(absl::StrCat(
"AI query failed: ", e.what()));
260 std::lock_guard<std::mutex> lock(mutex_);
265 std::lock_guard<std::mutex> lock(mutex_);
266 return last_proposal_id_;
270 mutable std::mutex mutex_;
271 std::unique_ptr<httplib::Client> client_;
278 std::string session_code_;
279 std::string username_;
280 std::string last_proposal_id_;
288 absl::Status
Connect(
const std::string&,
int) {
289 return absl::UnimplementedError(
"Network support requires JSON library");
292 absl::Status
JoinSession(
const std::string&,
const std::string&) {
293 return absl::UnimplementedError(
"Network support requires JSON library");
296 const std::string&) {
297 return absl::UnimplementedError(
"Network support requires JSON library");
300 return absl::UnimplementedError(
"Network support requires JSON library");
303 return absl::UnimplementedError(
"Network support requires JSON library");
305 absl::Status
SendMessage(
const std::string&,
const std::string&) {
306 return absl::UnimplementedError(
"Network support requires JSON library");
308 absl::StatusOr<std::string>
QueryAI(
const std::string&,
const std::string&) {
309 return absl::UnimplementedError(
"Network support requires JSON library");
326 return impl_->Connect(host, port);
330 const std::string& username) {
331 return impl_->JoinSession(session_code, username);
335 const std::string& proposal_json,
336 const std::string& username) {
337 return impl_->SubmitProposal(description, proposal_json, username);
341 const std::string& proposal_id) {
342 return impl_->GetProposalStatus(proposal_id);
346 const std::string& proposal_id,
int timeout_seconds) {
347 return impl_->WaitForApproval(proposal_id, timeout_seconds);
351 const std::string& sender) {
352 return impl_->SendMessage(message, sender);
356 const std::string& query,
const std::string& username) {
357 return impl_->QueryAI(query, username);
365 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)