clients/cpp: initial C++ SDK for clawdforge
Modern C++20 SDK targeting CMake 3.20+. Library is RAII / move-only, backed by a libcurl easy handle per Client. Public surface is throwing; exception hierarchy under clawdforge::Error covers AuthError, APIError (carries status_code + body), TransportError, and ProtocolError. Dependencies: libcurl + nlohmann/json (FetchContent or find_package). Tests use cpp-httplib's in-process server + doctest. 12 test cases / 70 assertions cover healthz, run with JSON / text / 502 / files, multipart upload, full token CRUD, transport failure, URL normalization, and bad-input rejection. Clean under -Wall -Wextra -Wpedantic -Werror, ASan + UBSan clean (no leaks, no UB). upload_file streams via curl_mime_filedata — no in-memory buffering. Install path produces clawdforge::clawdforge target consumable via target_link_libraries; FetchContent path mirrors the existing Rust / Go SDK ergonomics. MIT licensed.
This commit is contained in:
parent
a69e924592
commit
bae34a7701
12 changed files with 1866 additions and 0 deletions
78
clients/cpp/examples/basic.cpp
Normal file
78
clients/cpp/examples/basic.cpp
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
// SPDX-License-Identifier: MIT
|
||||
//
|
||||
// Minimal end-to-end example for the clawdforge C++ SDK. Set CF_BASE_URL and
|
||||
// CF_TOKEN in your environment, point them at a running clawdforge, build,
|
||||
// run.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <clawdforge/client.hpp>
|
||||
|
||||
namespace cf = clawdforge;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string env_or_default(const char* name, std::string fallback) {
|
||||
if (const char* v = std::getenv(name); v != nullptr && *v != '\0') {
|
||||
return std::string{v};
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
const std::string base_url = env_or_default("CF_BASE_URL", "http://localhost:8800");
|
||||
const std::string token = env_or_default("CF_TOKEN", "");
|
||||
if (token.empty()) {
|
||||
std::cerr << "set CF_TOKEN to a clawdforge bearer\n";
|
||||
return 2;
|
||||
}
|
||||
|
||||
cf::Client client{cf::ClientOptions{
|
||||
.base_url = base_url,
|
||||
.token = token,
|
||||
}};
|
||||
|
||||
try {
|
||||
const auto h = client.healthz();
|
||||
std::cout << "healthz ok=" << std::boolalpha << h.ok
|
||||
<< " claude_present=" << h.claude_present
|
||||
<< " version=" << h.claude_version << "\n";
|
||||
|
||||
const auto res = client.run(cf::RunRequest{
|
||||
.prompt = R"(Reply with JSON: {"hello": "world"})",
|
||||
.model = "sonnet",
|
||||
.timeout_secs = 60,
|
||||
});
|
||||
std::cout << "duration_ms=" << res.duration_ms << "\n";
|
||||
|
||||
if (const auto* j = std::get_if<nlohmann::json>(&res.result)) {
|
||||
std::cout << "json result: " << j->dump() << "\n";
|
||||
if (j->contains("hello")) {
|
||||
std::cout << "hello = " << j->at("hello").get<std::string>() << "\n";
|
||||
}
|
||||
} else if (const auto* s = std::get_if<std::string>(&res.result)) {
|
||||
std::cout << "text result: " << *s << "\n";
|
||||
}
|
||||
} catch (const cf::AuthError& e) {
|
||||
std::cerr << "auth: " << e.what() << "\n";
|
||||
return 3;
|
||||
} catch (const cf::APIError& e) {
|
||||
std::cerr << "api " << e.status_code() << ": " << e.what() << "\n";
|
||||
std::cerr << "body: " << e.body() << "\n";
|
||||
return 4;
|
||||
} catch (const cf::TransportError& e) {
|
||||
std::cerr << "transport: " << e.what() << "\n";
|
||||
return 5;
|
||||
} catch (const cf::Error& e) {
|
||||
std::cerr << "clawdforge: " << e.what() << "\n";
|
||||
return 6;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue