@bniladridas/cursor 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/.clang-tidy +28 -0
  2. package/.dockerignore +56 -0
  3. package/.env.example +29 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/ISSUE_TEMPLATE/blank.md +27 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. package/.github/SECURITY.md +24 -0
  9. package/.github/codeql/codeql-config.yml +8 -0
  10. package/.github/dependabot.yml +14 -0
  11. package/.github/labeler.yml +50 -0
  12. package/.github/packaging/brand-cursor.png +0 -0
  13. package/.github/packaging/database/init.sql +48 -0
  14. package/.github/packaging/docker/Dockerfile +111 -0
  15. package/.github/packaging/docker/docker-compose.yml +56 -0
  16. package/.github/packaging/scripts/preflight.sh +413 -0
  17. package/.github/packaging/scripts/prepare-release.sh +141 -0
  18. package/.github/packaging/scripts/release.sh +22 -0
  19. package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
  20. package/.github/pull_request_template.md +31 -0
  21. package/.github/signed.json +9 -0
  22. package/.github/workflows/README.md +23 -0
  23. package/.github/workflows/ci.yml +181 -0
  24. package/.github/workflows/cla.yml +33 -0
  25. package/.github/workflows/formula-sha.yml +63 -0
  26. package/.github/workflows/issue-response.yml +44 -0
  27. package/.github/workflows/labeler.yml +42 -0
  28. package/.github/workflows/pr-body.yml +49 -0
  29. package/.github/workflows/release.yml +176 -0
  30. package/.github/workflows/security.yml +94 -0
  31. package/.github/workflows/stale.yml +38 -0
  32. package/AGENTS.md +49 -0
  33. package/CHANGELOG.md +3 -0
  34. package/CMakeLists.txt +646 -0
  35. package/Formula/cursor.rb +46 -0
  36. package/LICENSE +201 -0
  37. package/Makefile +28 -0
  38. package/README.md +46 -0
  39. package/cli.js +16 -0
  40. package/include/agent.h +86 -0
  41. package/include/agent_mode.h +17 -0
  42. package/include/memory_manager.h +102 -0
  43. package/include/services/ai_service.h +31 -0
  44. package/include/services/auth_service.h +87 -0
  45. package/include/services/checkpoint_service.h +69 -0
  46. package/include/services/codebase_service.h +38 -0
  47. package/include/services/command_service.h +23 -0
  48. package/include/services/context_service.h +74 -0
  49. package/include/services/database_service.h +56 -0
  50. package/include/services/error_service.h +106 -0
  51. package/include/services/file_service.h +51 -0
  52. package/include/services/git_service.h +29 -0
  53. package/include/services/github_service.h +85 -0
  54. package/include/services/mcp_service.h +85 -0
  55. package/include/services/multi_file_service.h +93 -0
  56. package/include/services/sandbox_service.h +96 -0
  57. package/include/services/theme_service.h +67 -0
  58. package/include/services/web_service.h +52 -0
  59. package/include/utils/config.h +68 -0
  60. package/include/utils/memory_utils.h +79 -0
  61. package/include/utils/platform.h +56 -0
  62. package/include/utils/ui.h +43 -0
  63. package/include/utils/validation.h +63 -0
  64. package/include/utils/version.h.in +17 -0
  65. package/install.js +49 -0
  66. package/package.json +16 -0
  67. package/release/checksums.txt +3 -0
  68. package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
  69. package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
  70. package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
  71. package/src/agent.cpp +2026 -0
  72. package/src/main.cpp +97 -0
  73. package/src/memory_manager.cpp +814 -0
  74. package/src/services/ai_service.cpp +366 -0
  75. package/src/services/auth_service.cpp +779 -0
  76. package/src/services/checkpoint_service.cpp +465 -0
  77. package/src/services/codebase_service.cpp +233 -0
  78. package/src/services/command_service.cpp +82 -0
  79. package/src/services/context_service.cpp +348 -0
  80. package/src/services/database_service.cpp +148 -0
  81. package/src/services/error_service.cpp +438 -0
  82. package/src/services/file_service.cpp +349 -0
  83. package/src/services/git_service.cpp +148 -0
  84. package/src/services/github_service.cpp +435 -0
  85. package/src/services/mcp_service.cpp +481 -0
  86. package/src/services/multi_file_service.cpp +591 -0
  87. package/src/services/sandbox_service.cpp +678 -0
  88. package/src/services/theme_service.cpp +429 -0
  89. package/src/services/web_service.cpp +532 -0
  90. package/src/utils/config.cpp +77 -0
  91. package/src/utils/memory_utils.cpp +93 -0
  92. package/src/utils/ui.cpp +307 -0
  93. package/src/utils/validation.cpp +306 -0
  94. package/src/utils/version.cpp +175 -0
  95. package/tests/e2e/docker-compose.yml +195 -0
  96. package/tests/e2e/run_e2e_tests.sh +70 -0
  97. package/tests/e2e/run_tests_in_docker.sh +115 -0
  98. package/tests/main_test.cpp +16 -0
  99. package/tests/mocks/mock_ollama.py +98 -0
  100. package/tests/mocks/start_nginx.sh +64 -0
@@ -0,0 +1,532 @@
1
+ #include "services/web_service.h"
2
+ #include "utils/config.h"
3
+ #include <algorithm>
4
+ #include <cpr/cpr.h>
5
+ #include <curl/curl.h>
6
+ #include <iostream>
7
+ #include <nlohmann/json.hpp>
8
+ #include <sstream>
9
+ #include <utility>
10
+ #include <vector>
11
+
12
+ struct CurlHandle {
13
+ CURL *handle;
14
+ CurlHandle() : handle(curl_easy_init()) {}
15
+ ~CurlHandle() {
16
+ if (handle)
17
+ curl_easy_cleanup(handle);
18
+ }
19
+ CurlHandle(const CurlHandle &) = delete;
20
+ CurlHandle &operator=(const CurlHandle &) = delete;
21
+ CURL *get() const { return handle; }
22
+ explicit operator bool() const { return handle != nullptr; }
23
+ };
24
+
25
+ const long kDefaultTimeoutSeconds = 30;
26
+ const size_t kMaxContentLength = 8000;
27
+
28
+ // Callback function for writing response data
29
+ static size_t WriteCallback(void *contents, size_t size, size_t nmemb,
30
+ void *userp) {
31
+ size_t real_size = size * nmemb;
32
+ std::string *response = static_cast<std::string *>(userp);
33
+ // Reserve space to avoid frequent reallocations
34
+ if (response->capacity() < response->size() + real_size) {
35
+ response->reserve(response->size() + real_size + 4096);
36
+ }
37
+ response->append(static_cast<char *>(contents), real_size);
38
+ return real_size;
39
+ }
40
+
41
+ // Callback function for writing header data
42
+ static size_t HeaderCallback(char *buffer, size_t size, size_t nitems,
43
+ void *userdata) {
44
+ size_t real_size = size * nitems;
45
+ std::string *headers = static_cast<std::string *>(userdata);
46
+ headers->append(buffer, real_size);
47
+ return real_size;
48
+ }
49
+
50
+ namespace Services {
51
+ std::string WebService::get_api_key() {
52
+ return Utils::Config::get_env_var("SERPAPI_KEY");
53
+ }
54
+
55
+ bool WebService::is_available() { return !get_api_key().empty(); }
56
+
57
+ std::string WebService::search(const std::string &query) {
58
+
59
+ try {
60
+ std::string api_key = get_api_key();
61
+ if (api_key.empty()) {
62
+ return "Error: SERPAPI_KEY is missing.";
63
+ }
64
+
65
+ // URL encode the query
66
+ std::string encoded_query;
67
+ {
68
+ CurlHandle curl;
69
+ if (curl) {
70
+ char *output = curl_easy_escape(curl.get(), query.c_str(),
71
+ static_cast<int>(query.length()));
72
+ if (output) {
73
+ encoded_query = output;
74
+ curl_free(output);
75
+ }
76
+ } else {
77
+ encoded_query = query;
78
+ }
79
+ }
80
+
81
+ std::string search_url = "https://api.duckduckgo.com/?q=" + encoded_query +
82
+ "&api_key=" + api_key;
83
+
84
+ std::string response_str;
85
+ std::string response_headers;
86
+
87
+ {
88
+ CurlHandle search_curl;
89
+ if (search_curl) {
90
+ curl_easy_setopt(search_curl.get(), CURLOPT_URL, search_url.c_str());
91
+ curl_easy_setopt(search_curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
92
+ curl_easy_setopt(search_curl.get(), CURLOPT_WRITEDATA, &response_str);
93
+ curl_easy_setopt(search_curl.get(), CURLOPT_HEADERFUNCTION, HeaderCallback);
94
+ curl_easy_setopt(search_curl.get(), CURLOPT_HEADERDATA, &response_headers);
95
+ curl_easy_setopt(search_curl.get(), CURLOPT_TIMEOUT, 5L);
96
+ curl_easy_setopt(search_curl.get(), CURLOPT_USERAGENT, "Cursor-Agent/1.0");
97
+
98
+ CURLcode res = curl_easy_perform(search_curl.get());
99
+ if (res != CURLE_OK) {
100
+ std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res)
101
+ << std::endl;
102
+ return "Error: Failed to perform web search";
103
+ }
104
+
105
+ long http_code = 0;
106
+ curl_easy_getinfo(search_curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
107
+ if (http_code != 200) {
108
+ std::cerr << "HTTP request failed with code: " << http_code
109
+ << std::endl;
110
+ return "Error: Failed to perform web search (HTTP " +
111
+ std::to_string(http_code) + ")";
112
+ }
113
+ } else {
114
+ return "Error: Failed to initialize cURL";
115
+ }
116
+ }
117
+
118
+ // Parse JSON response
119
+ try {
120
+ auto json = nlohmann::json::parse(response_str);
121
+ std::string result;
122
+
123
+ // Extract and format the search results
124
+ if (json.contains("AbstractText") && !json["AbstractText"].is_null()) {
125
+ result = json["AbstractText"].get<std::string>();
126
+ if (!result.empty()) {
127
+ return "Abstract: " + result;
128
+ }
129
+ }
130
+
131
+ if (json.contains("RelatedTopics") && json["RelatedTopics"].is_array()) {
132
+ for (const auto &topic : json["RelatedTopics"]) {
133
+ if (topic.contains("Text")) {
134
+ result += "• " + topic["Text"].get<std::string>() + "\n";
135
+ }
136
+ }
137
+ }
138
+
139
+ return result.empty() ? "No results found." : result;
140
+ } catch (const std::exception &e) {
141
+ return "Error parsing search results: " + std::string(e.what());
142
+ }
143
+
144
+ } catch (const std::exception &e) {
145
+ return std::string("Error performing search: ") + e.what();
146
+ }
147
+ }
148
+
149
+ bool WebService::is_valid_url(const std::string &url) {
150
+ // Basic URL validation
151
+ if (url.empty())
152
+ return false;
153
+
154
+ // Check for common protocols
155
+ if (url.find("http://") == 0 || url.find("https://") == 0) {
156
+ // Must have at least a domain after protocol
157
+ size_t domain_start = url.find("://") + 3;
158
+ if (domain_start < url.length() &&
159
+ url.find('.', domain_start) != std::string::npos) {
160
+ return true;
161
+ }
162
+ }
163
+ return false;
164
+ }
165
+
166
+ std::string WebService::extract_text_content(const std::string &html) {
167
+ std::string text = html;
168
+
169
+ // Remove script and style tags and their content
170
+ size_t pos = 0;
171
+ while ((pos = text.find("<script", pos)) != std::string::npos) {
172
+ size_t end = text.find("</script>", pos);
173
+ if (end != std::string::npos) {
174
+ text.erase(pos, end - pos + 9);
175
+ } else {
176
+ break;
177
+ }
178
+ }
179
+
180
+ pos = 0;
181
+ while ((pos = text.find("<style", pos)) != std::string::npos) {
182
+ size_t end = text.find("</style>", pos);
183
+ if (end != std::string::npos) {
184
+ text.erase(pos, end - pos + 8);
185
+ } else {
186
+ break;
187
+ }
188
+ }
189
+
190
+ // Remove HTML tags
191
+ pos = 0;
192
+ while ((pos = text.find('<', pos)) != std::string::npos) {
193
+ size_t end = text.find('>', pos);
194
+ if (end != std::string::npos) {
195
+ text.erase(pos, end - pos + 1);
196
+ } else {
197
+ break;
198
+ }
199
+ }
200
+
201
+ return sanitize_content(text);
202
+ }
203
+
204
+ std::string WebService::sanitize_content(const std::string &content) {
205
+ std::string sanitized = content;
206
+
207
+ // Replace HTML entities
208
+ size_t pos = 0;
209
+ while ((pos = sanitized.find("&amp;", pos)) != std::string::npos) {
210
+ sanitized.replace(pos, 5, "&");
211
+ pos += 1;
212
+ }
213
+ while ((pos = sanitized.find("&lt;", pos)) != std::string::npos) {
214
+ sanitized.replace(pos, 4, "<");
215
+ pos += 1;
216
+ }
217
+ while ((pos = sanitized.find("&gt;", pos)) != std::string::npos) {
218
+ sanitized.replace(pos, 4, ">");
219
+ pos += 1;
220
+ }
221
+ while ((pos = sanitized.find("&quot;", pos)) != std::string::npos) {
222
+ sanitized.replace(pos, 6, "\"");
223
+ pos += 1;
224
+ }
225
+ while ((pos = sanitized.find("&#39;", pos)) != std::string::npos) {
226
+ sanitized.replace(pos, 5, "'");
227
+ pos += 1;
228
+ }
229
+
230
+ // Clean up whitespace
231
+ pos = 0;
232
+ while ((pos = sanitized.find("\n\n\n", pos)) != std::string::npos) {
233
+ sanitized.replace(pos, 3, "\n\n");
234
+ }
235
+
236
+ // Trim leading/trailing whitespace
237
+ size_t start = sanitized.find_first_not_of(" \t\n\r");
238
+ if (start == std::string::npos)
239
+ return "";
240
+
241
+ size_t end = sanitized.find_last_not_of(" \t\n\r");
242
+ return sanitized.substr(start, end - start + 1);
243
+ }
244
+
245
+ WebResponse WebService::fetch_url(const std::string &url) {
246
+ WebResponse response;
247
+ response.success = false;
248
+
249
+ if (!is_valid_url(url)) {
250
+ response.error_message = "Invalid URL format";
251
+ response.status_code = 0;
252
+ return response;
253
+ }
254
+
255
+ CurlHandle curl;
256
+ if (!curl) {
257
+ response.error_message = "Failed to initialize cURL";
258
+ return response;
259
+ }
260
+
261
+ try {
262
+ std::string response_body;
263
+ std::string response_headers;
264
+ long http_code = 0;
265
+
266
+ // Set up curl options
267
+ curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
268
+ curl_easy_setopt(curl.get(), CURLOPT_USERAGENT, "Cursor-Agent/1.0");
269
+ curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, 10L);
270
+ curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
271
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
272
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_body);
273
+ curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, HeaderCallback);
274
+ curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &response_headers);
275
+
276
+ // Perform the request
277
+ CURLcode res = curl_easy_perform(curl.get());
278
+
279
+ if (res != CURLE_OK) {
280
+ response.error_message =
281
+ "cURL error: " + std::string(curl_easy_strerror(res));
282
+ return response;
283
+ }
284
+
285
+ // Get HTTP status code
286
+ curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
287
+
288
+ // Process response
289
+ response.status_code = static_cast<int>(http_code);
290
+ response.content = response_body;
291
+ response.success = (http_code >= 200 && http_code < 300);
292
+
293
+ // Parse headers
294
+ std::istringstream header_stream(response_headers);
295
+ std::string header_line;
296
+ while (std::getline(header_stream, header_line, '\n')) {
297
+ size_t colon_pos = header_line.find(':');
298
+ if (colon_pos != std::string::npos) {
299
+ std::string key = header_line.substr(0, colon_pos);
300
+ std::string value = header_line.substr(colon_pos + 1);
301
+
302
+ // Trim whitespace
303
+ key.erase(key.begin(), std::find_if(key.begin(), key.end(), [](int ch) {
304
+ return !std::isspace(ch);
305
+ }));
306
+ key.erase(std::find_if(key.rbegin(), key.rend(),
307
+ [](int ch) { return !std::isspace(ch); })
308
+ .base(),
309
+ key.end());
310
+
311
+ value.erase(value.begin(),
312
+ std::find_if(value.begin(), value.end(),
313
+ [](int ch) { return !std::isspace(ch); }));
314
+ value.erase(std::find_if(value.rbegin(), value.rend(),
315
+ [](int ch) { return !std::isspace(ch); })
316
+ .base(),
317
+ value.end());
318
+
319
+ if (!key.empty()) {
320
+ response.headers[key] = value;
321
+ }
322
+ }
323
+ }
324
+
325
+ // Set content type
326
+ auto content_type_it = response.headers.find("content-type");
327
+ if (content_type_it != response.headers.end()) {
328
+ response.content_type = content_type_it->second;
329
+ }
330
+
331
+ if (!response.success) {
332
+ response.error_message = "HTTP " + std::to_string(response.status_code);
333
+ }
334
+
335
+ } catch (const std::exception &e) {
336
+ response.error_message = "Exception: " + std::string(e.what());
337
+ response.success = false;
338
+
339
+ } catch (...) {
340
+ response.error_message = "Unknown exception";
341
+ response.success = false;
342
+ }
343
+
344
+ return response;
345
+ }
346
+
347
+ std::string WebService::fetch_text(const std::string &url) {
348
+ WebResponse response = fetch_url(url);
349
+
350
+ if (!response.success) {
351
+ return "Error fetching URL: " + response.error_message;
352
+ }
353
+
354
+ // Check if content is HTML and extract text
355
+ if (response.content_type.find("text/html") != std::string::npos) {
356
+ std::string extracted = extract_text_content(response.content);
357
+ if (extracted.length() > kMaxContentLength) {
358
+ extracted = extracted.substr(0, kMaxContentLength) +
359
+ "\n\n[Content truncated - showing first " +
360
+ std::to_string(kMaxContentLength) + " characters]";
361
+ }
362
+ return extracted;
363
+ }
364
+
365
+ // For plain text or other content types
366
+ if (response.content.length() > kMaxContentLength) {
367
+ return response.content.substr(0, kMaxContentLength) +
368
+ "\n\n[Content truncated - showing first " +
369
+ std::to_string(kMaxContentLength) + " characters]";
370
+ }
371
+
372
+ return response.content;
373
+ }
374
+
375
+ std::string WebService::fetch_json(const std::string &url) {
376
+ try {
377
+ auto response = fetch_url(url);
378
+ if (response.status_code == 200) {
379
+ // Validate JSON
380
+ auto json = nlohmann::json::parse(response.content);
381
+ // Pretty print JSON with truncation if needed
382
+ std::string formatted = json.dump(2);
383
+ if (formatted.length() > kMaxContentLength) {
384
+ formatted = formatted.substr(0, kMaxContentLength) +
385
+ "\n\n[JSON truncated - showing first " +
386
+ std::to_string(kMaxContentLength) + " characters]";
387
+ }
388
+ return formatted;
389
+ } else {
390
+ return "Error: " + std::to_string(response.status_code) + " - " +
391
+ response.error_message;
392
+ }
393
+ } catch (const std::exception &e) {
394
+ return "Error: " + std::string(e.what());
395
+ }
396
+ }
397
+
398
+ WebResponse WebService::fetch_with_headers(const std::string &url,
399
+ const HeaderMap &headers) {
400
+ CurlHandle curl;
401
+ WebResponse response;
402
+
403
+ if (!curl) {
404
+ response.status_code = 0;
405
+ response.error_message = "Failed to initialize CURL";
406
+ response.success = false;
407
+ return response;
408
+ }
409
+
410
+ std::string response_body;
411
+ std::string response_headers;
412
+
413
+ // Add custom headers to the request
414
+ struct curl_slist *header_list = nullptr;
415
+ for (const auto &[key, value] : headers) {
416
+ std::string header = key + ": " + value;
417
+ header_list = curl_slist_append(header_list, header.c_str());
418
+ }
419
+
420
+ // Set CURL options
421
+ curl_easy_setopt(curl.get(), CURLOPT_URL, url.c_str());
422
+ curl_easy_setopt(curl.get(), CURLOPT_HTTPHEADER, header_list);
423
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEFUNCTION, WriteCallback);
424
+ curl_easy_setopt(curl.get(), CURLOPT_WRITEDATA, &response_body);
425
+ curl_easy_setopt(curl.get(), CURLOPT_HEADERFUNCTION, HeaderCallback);
426
+ curl_easy_setopt(curl.get(), CURLOPT_HEADERDATA, &response_headers);
427
+ curl_easy_setopt(curl.get(), CURLOPT_FOLLOWLOCATION, 1L);
428
+ curl_easy_setopt(curl.get(), CURLOPT_TIMEOUT, kDefaultTimeoutSeconds);
429
+
430
+ // Perform the request
431
+ CURLcode res = curl_easy_perform(curl.get());
432
+
433
+ // Get the HTTP status code
434
+ long http_code = 0;
435
+ curl_easy_getinfo(curl.get(), CURLINFO_RESPONSE_CODE, &http_code);
436
+
437
+ // Get content type if available
438
+ char *content_type = nullptr;
439
+ curl_easy_getinfo(curl.get(), CURLINFO_CONTENT_TYPE, &content_type);
440
+
441
+ // Clean up
442
+ if (header_list) {
443
+ curl_slist_free_all(header_list);
444
+ }
445
+
446
+ // Set response properties
447
+ response.status_code = static_cast<int>(http_code);
448
+ response.content = response_body;
449
+ response.content_type = content_type ? content_type : "";
450
+ response.success = (res == CURLE_OK);
451
+
452
+ if (res != CURLE_OK) {
453
+ response.error_message = curl_easy_strerror(res);
454
+ } else if (http_code >= 400) {
455
+ response.error_message = "HTTP error " + std::to_string(http_code);
456
+ }
457
+
458
+ // Parse response headers
459
+ std::istringstream header_stream(response_headers);
460
+ std::string header_line;
461
+ while (std::getline(header_stream, header_line)) {
462
+ size_t colon_pos = header_line.find(':');
463
+ if (colon_pos != std::string::npos) {
464
+ std::string key = header_line.substr(0, colon_pos);
465
+ std::string value = header_line.substr(colon_pos + 1);
466
+ // Trim whitespace
467
+ key.erase(0, key.find_first_not_of(" \t"));
468
+ key.erase(key.find_last_not_of(" \t") + 1);
469
+ value.erase(0, value.find_first_not_of(" \t"));
470
+ value.erase(value.find_last_not_of(" \t") + 1);
471
+ response.headers[key] = value;
472
+ }
473
+ }
474
+
475
+ return response;
476
+ }
477
+
478
+ WebResponse WebService::post_json(const std::string &url,
479
+ const std::string &json_body,
480
+ const HeaderMap &headers) {
481
+ WebResponse response;
482
+ response.success = false;
483
+
484
+ try {
485
+ // Prepare headers for CPR
486
+ cpr::Header cpr_headers;
487
+ for (const auto &[key, value] : headers) {
488
+ cpr_headers[key] = value;
489
+ }
490
+
491
+ // Set Content-Type if not provided
492
+ if (headers.find("Content-Type") == headers.end()) {
493
+ cpr_headers["Content-Type"] = "application/json";
494
+ }
495
+
496
+ // Make POST request
497
+ cpr::Response cpr_response =
498
+ cpr::Post(cpr::Url{url}, cpr_headers, cpr::Body{json_body},
499
+ cpr::Timeout{std::chrono::seconds{kDefaultTimeoutSeconds}});
500
+
501
+ // Fill response
502
+ response.status_code = static_cast<int>(cpr_response.status_code);
503
+ response.content = cpr_response.text;
504
+ response.success =
505
+ (cpr_response.status_code >= 200 && cpr_response.status_code < 300);
506
+
507
+ if (!response.success) {
508
+ response.error_message = "HTTP " +
509
+ std::to_string(cpr_response.status_code) +
510
+ " | Error: " + cpr_response.error.message;
511
+ }
512
+
513
+ // Parse headers
514
+ for (const auto &[key, value] : cpr_response.header) {
515
+ response.headers[key] = value;
516
+ }
517
+
518
+ // Set content type
519
+ auto content_type_it = response.headers.find("content-type");
520
+ if (content_type_it != response.headers.end()) {
521
+ response.content_type = content_type_it->second;
522
+ }
523
+
524
+ } catch (const std::exception &e) {
525
+ response.error_message = "Exception: " + std::string(e.what());
526
+ } catch (...) {
527
+ response.error_message = "Unknown exception";
528
+ }
529
+
530
+ return response;
531
+ }
532
+ } // namespace Services
@@ -0,0 +1,77 @@
1
+ #include "utils/config.h"
2
+
3
+ // Define the CURSOR_LIBRARY macro to ensure proper symbol export
4
+ #ifndef CURSOR_LIBRARY
5
+ #define CURSOR_LIBRARY
6
+ #endif
7
+ #include <cstdlib>
8
+ #include <fstream>
9
+ #include <iostream>
10
+ #include <sstream>
11
+
12
+ namespace Utils {
13
+ namespace Config {
14
+
15
+ static void set_env_var(const std::string &key, const std::string &value) {
16
+ #if defined(_WIN32)
17
+ _putenv_s(key.c_str(), value.c_str());
18
+ #else
19
+ setenv(key.c_str(), value.c_str(), 1);
20
+ #endif
21
+ }
22
+
23
+ void load_environment(const std::string &filename) {
24
+ std::ifstream file(filename);
25
+ if (!file.is_open()) {
26
+ return; // Optional
27
+ }
28
+
29
+ std::string line;
30
+ while (std::getline(file, line)) {
31
+ // Trim leading/trailing spaces
32
+ auto trim = [](std::string &s) {
33
+ s.erase(0, s.find_first_not_of(" \t\r\n"));
34
+ s.erase(s.find_last_not_of(" \t\r\n") + 1);
35
+ };
36
+
37
+ trim(line);
38
+ if (line.empty() || line[0] == '#')
39
+ continue;
40
+
41
+ // Remove inline comment if not inside quotes
42
+ size_t hash_pos = line.find('#');
43
+ if (hash_pos != std::string::npos && (line.find('"') > hash_pos)) {
44
+ line = line.substr(0, hash_pos);
45
+ trim(line);
46
+ }
47
+
48
+ std::istringstream is_line(line);
49
+ std::string key, value;
50
+
51
+ if (std::getline(is_line, key, '=') && std::getline(is_line, value)) {
52
+ trim(key);
53
+ trim(value);
54
+
55
+ // Remove quotes
56
+ if (value.size() >= 2 && value.front() == '"' && value.back() == '"') {
57
+ value = value.substr(1, value.size() - 2);
58
+ }
59
+
60
+ if (!key.empty()) {
61
+ set_env_var(key, value);
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ std::string get_env_var(const std::string &key,
68
+ const std::string &default_value) {
69
+ const char *value = std::getenv(key.c_str());
70
+ return value ? std::string(value) : default_value;
71
+ }
72
+
73
+ bool has_env_var(const std::string &key) {
74
+ return std::getenv(key.c_str()) != nullptr;
75
+ }
76
+ } // namespace Config
77
+ } // namespace Utils
@@ -0,0 +1,93 @@
1
+ #include "utils/memory_utils.h"
2
+ #include <algorithm>
3
+ #include <array>
4
+ #include <iomanip>
5
+ #include <sstream>
6
+ #include <string>
7
+
8
+ namespace Utils {
9
+ namespace Memory {
10
+
11
+ size_t MemoryTracker::current_usage_ = 0;
12
+ size_t MemoryTracker::peak_usage_ = 0;
13
+
14
+ void MemoryTracker::add_allocation(size_t size) {
15
+ current_usage_ += size;
16
+ peak_usage_ = std::max(peak_usage_, current_usage_);
17
+ }
18
+
19
+ void MemoryTracker::remove_allocation(size_t size) {
20
+ if (current_usage_ >= size) {
21
+ current_usage_ -= size;
22
+ }
23
+ }
24
+
25
+ std::string MemoryTracker::format_bytes(size_t bytes) {
26
+ constexpr std::array<std::string_view, 4> units = {"B", "KB", "MB", "GB"};
27
+ constexpr double KB = 1024.0;
28
+ double size = static_cast<double>(bytes);
29
+ size_t unit_index = 0;
30
+
31
+ while (size >= KB && unit_index < 3) {
32
+ size /= KB;
33
+ unit_index++;
34
+ }
35
+
36
+ std::ostringstream oss;
37
+ oss << std::fixed << std::setprecision(2) << size << " " << units[unit_index];
38
+ return oss.str();
39
+ }
40
+
41
+ std::vector<std::string_view> split_view(std::string_view input,
42
+ char delimiter) {
43
+ std::vector<std::string_view> result;
44
+ result.reserve(8); // Reserve for common case
45
+
46
+ size_t start = 0;
47
+ size_t pos = 0;
48
+
49
+ while ((pos = input.find(delimiter, start)) != std::string_view::npos) {
50
+ if (pos > start) { // Skip empty tokens
51
+ result.emplace_back(input.substr(start, pos - start));
52
+ }
53
+ start = pos + 1;
54
+ }
55
+
56
+ // Add the last token if it exists
57
+ if (start < input.length()) {
58
+ result.emplace_back(input.substr(start));
59
+ }
60
+
61
+ return result;
62
+ }
63
+
64
+ std::vector<std::string_view> split_view(std::string_view input,
65
+ std::string_view delimiter) {
66
+ std::vector<std::string_view> result;
67
+ result.reserve(8); // Reserve for common case
68
+
69
+ if (delimiter.empty()) {
70
+ result.emplace_back(input);
71
+ return result;
72
+ }
73
+
74
+ size_t start = 0;
75
+ size_t pos = 0;
76
+
77
+ while ((pos = input.find(delimiter, start)) != std::string_view::npos) {
78
+ if (pos > start) { // Skip empty tokens
79
+ result.emplace_back(input.substr(start, pos - start));
80
+ }
81
+ start = pos + delimiter.length();
82
+ }
83
+
84
+ // Add the last token if it exists
85
+ if (start < input.length()) {
86
+ result.emplace_back(input.substr(start));
87
+ }
88
+
89
+ return result;
90
+ }
91
+
92
+ } // namespace Memory
93
+ } // namespace Utils