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.
78 lines
2.4 KiB
C++
78 lines
2.4 KiB
C++
// 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;
|
|
}
|