webxos commited on
Commit
5ae1c1d
·
verified ·
1 Parent(s): 664bf7e

Delete shadowclaw.c

Browse files
Files changed (1) hide show
  1. shadowclaw.c +0 -559
shadowclaw.c DELETED
@@ -1,559 +0,0 @@
1
- #define _GNU_SOURCE
2
- #include <stdio.h>
3
- #include <stdlib.h>
4
- #include <stdint.h>
5
- #include <string.h>
6
- #include <stdbool.h>
7
- #include <unistd.h>
8
- #include <time.h>
9
- #include <curl/curl.h>
10
- #include "cJSON.h"
11
-
12
- // --------------------------------------------------------------------
13
- // Shadow Header + Arena (Tsoding/stb_ds style)
14
- // --------------------------------------------------------------------
15
- typedef struct {
16
- size_t capacity; // bytes available AFTER header
17
- size_t length; // bytes used AFTER header
18
- uint64_t tag; // magic: 0x534841444F57434C = "SHADOWCL"
19
- uint32_t version;
20
- uint32_t flags; // bit 0 = dirty
21
- } ShadowHeader;
22
-
23
- #define SHADOW_MAGIC 0x534841444F57434CULL
24
- #define SHADOW_SIZE (sizeof(ShadowHeader))
25
- #define ALIGN_UP(x, a) (((x) + (a)-1) & ~((a)-1))
26
-
27
- static inline ShadowHeader* shadow_header(void *data) {
28
- return (ShadowHeader*)((char*)data - SHADOW_SIZE);
29
- }
30
-
31
- typedef struct {
32
- void *data; // user‑facing payload pointer
33
- size_t reserved; // total malloc size (header + capacity)
34
- } ShadowArena;
35
-
36
- ShadowArena shadow_arena_create(size_t initial_capacity) {
37
- ShadowArena a = {0};
38
- size_t total = SHADOW_SIZE + initial_capacity;
39
- total = ALIGN_UP(total, 64);
40
-
41
- ShadowHeader *h = malloc(total);
42
- if (!h) abort();
43
-
44
- *h = (ShadowHeader){
45
- .capacity = initial_capacity,
46
- .length = 0,
47
- .tag = SHADOW_MAGIC,
48
- .version = 1,
49
- .flags = 0
50
- };
51
- a.data = (char*)h + SHADOW_SIZE;
52
- a.reserved = total;
53
- return a;
54
- }
55
-
56
- void shadow_arena_destroy(ShadowArena *a) {
57
- if (a->data) {
58
- free(shadow_header(a->data));
59
- a->data = NULL;
60
- }
61
- }
62
-
63
- void* shadow_arena_push(ShadowArena *a, const void *src, size_t bytes) {
64
- ShadowHeader *h = shadow_header(a->data);
65
-
66
- if (h->length + bytes > h->capacity) {
67
- size_t new_cap = h->capacity ? h->capacity * 2 : 4096;
68
- while (new_cap < h->length + bytes) new_cap *= 2;
69
- size_t new_total = SHADOW_SIZE + new_cap;
70
- new_total = ALIGN_UP(new_total, 64);
71
-
72
- ShadowHeader *new_h = realloc(h, new_total);
73
- if (!new_h) abort();
74
-
75
- new_h->capacity = new_cap;
76
- a->data = (char*)new_h + SHADOW_SIZE;
77
- a->reserved = new_total;
78
- h = new_h;
79
- }
80
-
81
- char *dst = (char*)a->data + h->length;
82
- if (src) memcpy(dst, src, bytes);
83
- else memset(dst, 0, bytes);
84
-
85
- h->length += bytes;
86
- h->flags |= 1; // dirty
87
- return dst;
88
- }
89
-
90
- size_t shadow_arena_len(const ShadowArena *a) {
91
- return shadow_header(a->data)->length;
92
- }
93
-
94
- void shadow_arena_clear(ShadowArena *a) {
95
- ShadowHeader *h = shadow_header(a->data);
96
- h->length = 0;
97
- h->flags &= ~1;
98
- }
99
-
100
- // --------------------------------------------------------------------
101
- // Blob Format (tagged, length‑prefixed items)
102
- // --------------------------------------------------------------------
103
- typedef struct {
104
- uint32_t size; // payload size (excluding this header)
105
- uint32_t kind; // 1=system,2=user,3=assistant,4=tool_call,5=tool_result,6=memory
106
- uint64_t id; // unique id (timestamp or counter)
107
- } BlobHeader;
108
-
109
- // Append a typed blob – returns offset from arena->data start
110
- ptrdiff_t blob_append(ShadowArena *a, uint32_t kind, uint64_t id,
111
- const void *payload, size_t payload_bytes)
112
- {
113
- size_t total = sizeof(BlobHeader) + payload_bytes;
114
- char *p = shadow_arena_push(a, NULL, total);
115
-
116
- BlobHeader bh = {
117
- .size = (uint32_t)payload_bytes,
118
- .kind = kind,
119
- .id = id
120
- };
121
- memcpy(p, &bh, sizeof(bh));
122
- if (payload_bytes) memcpy(p + sizeof(bh), payload, payload_bytes);
123
- return p - (char*)a->data;
124
- }
125
-
126
- // Iterate over all blobs: calls `f(blob_header, payload, userdata)`
127
- void blob_foreach(ShadowArena *a,
128
- void (*f)(const BlobHeader*, const char*, void*),
129
- void *userdata)
130
- {
131
- char *start = a->data;
132
- size_t len = shadow_header(a->data)->length;
133
- char *end = start + len;
134
- char *p = start;
135
- while (p < end) {
136
- BlobHeader *bh = (BlobHeader*)p;
137
- char *payload = p + sizeof(BlobHeader);
138
- f(bh, payload, userdata);
139
- p += sizeof(BlobHeader) + bh->size;
140
- }
141
- }
142
-
143
- // --------------------------------------------------------------------
144
- // Persistence: save / load the whole arena to a file
145
- // --------------------------------------------------------------------
146
- bool arena_save(const ShadowArena *a, const char *filename) {
147
- FILE *f = fopen(filename, "wb");
148
- if (!f) return false;
149
- // write the whole malloc block (header + payload)
150
- size_t n = fwrite(shadow_header(a->data), 1, a->reserved, f);
151
- fclose(f);
152
- return n == a->reserved;
153
- }
154
-
155
- bool arena_load(ShadowArena *a, const char *filename) {
156
- FILE *f = fopen(filename, "rb");
157
- if (!f) return false;
158
-
159
- fseek(f, 0, SEEK_END);
160
- long size = ftell(f);
161
- fseek(f, 0, SEEK_SET);
162
- if (size < (long)SHADOW_SIZE) {
163
- fclose(f);
164
- return false;
165
- }
166
-
167
- void *block = malloc(size);
168
- if (!block) { fclose(f); return false; }
169
- if (fread(block, 1, size, f) != (size_t)size) {
170
- free(block);
171
- fclose(f);
172
- return false;
173
- }
174
- fclose(f);
175
-
176
- // validate magic
177
- ShadowHeader *h = (ShadowHeader*)block;
178
- if (h->tag != SHADOW_MAGIC || h->version != 1) {
179
- free(block);
180
- return false;
181
- }
182
-
183
- // destroy old arena and replace
184
- shadow_arena_destroy(a);
185
- a->data = (char*)block + SHADOW_SIZE;
186
- a->reserved = size;
187
- return true;
188
- }
189
-
190
- // --------------------------------------------------------------------
191
- // Tools
192
- // --------------------------------------------------------------------
193
- typedef struct {
194
- char *name;
195
- char *(*func)(const char *args); // returns newly allocated string
196
- } Tool;
197
-
198
- // tool: shell command execution
199
- char* tool_shell(const char *args) {
200
- FILE *fp = popen(args, "r");
201
- if (!fp) return strdup("error: popen failed");
202
- char *result = NULL;
203
- size_t len = 0;
204
- FILE *out = open_memstream(&result, &len);
205
- char buf[256];
206
- while (fgets(buf, sizeof(buf), fp)) fputs(buf, out);
207
- pclose(fp);
208
- fclose(out);
209
- return result ? result : strdup("");
210
- }
211
-
212
- // tool: read file
213
- char* tool_read_file(const char *args) {
214
- FILE *fp = fopen(args, "rb");
215
- if (!fp) return strdup("error: cannot open file");
216
- char *content = NULL;
217
- size_t len = 0;
218
- FILE *out = open_memstream(&content, &len);
219
- char buf[4096];
220
- size_t n;
221
- while ((n = fread(buf, 1, sizeof(buf), fp)) > 0)
222
- fwrite(buf, 1, n, out);
223
- fclose(fp);
224
- fclose(out);
225
- return content ? content : strdup("");
226
- }
227
-
228
- // tool: write file (args: "filename\ncontent")
229
- char* tool_write_file(const char *args) {
230
- char *filename = strdup(args);
231
- char *newline = strchr(filename, '\n');
232
- if (!newline) {
233
- free(filename);
234
- return strdup("error: missing newline separator");
235
- }
236
- *newline = '\0';
237
- char *content = newline + 1;
238
- FILE *fp = fopen(filename, "wb");
239
- if (!fp) {
240
- free(filename);
241
- return strdup("error: cannot write file");
242
- }
243
- fwrite(content, 1, strlen(content), fp);
244
- fclose(fp);
245
- free(filename);
246
- return strdup("ok");
247
- }
248
-
249
- // tool: HTTP GET (args = URL)
250
- size_t write_cb(void *ptr, size_t size, size_t nmemb, void *stream) {
251
- size_t total = size * nmemb;
252
- fwrite(ptr, 1, total, (FILE*)stream);
253
- return total;
254
- }
255
- char* tool_http_get(const char *args) {
256
- CURL *curl = curl_easy_init();
257
- if (!curl) return strdup("error: curl init failed");
258
-
259
- char *response = NULL;
260
- size_t len = 0;
261
- FILE *out = open_memstream(&response, &len);
262
-
263
- curl_easy_setopt(curl, CURLOPT_URL, args);
264
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb);
265
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, out);
266
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
267
- CURLcode res = curl_easy_perform(curl);
268
- if (res != CURLE_OK) {
269
- fclose(out);
270
- curl_easy_cleanup(curl);
271
- return strdup("error: curl failed");
272
- }
273
- fclose(out);
274
- curl_easy_cleanup(curl);
275
- return response ? response : strdup("");
276
- }
277
-
278
- // tool: math expression (using bc)
279
- char* tool_math(const char *args) {
280
- char cmd[4096];
281
- snprintf(cmd, sizeof(cmd), "echo '%s' | bc 2>/dev/null", args);
282
- FILE *fp = popen(cmd, "r");
283
- if (!fp) return strdup("error: bc failed");
284
- char *result = NULL;
285
- size_t len = 0;
286
- FILE *out = open_memstream(&result, &len);
287
- char buf[256];
288
- while (fgets(buf, sizeof(buf), fp)) fputs(buf, out);
289
- pclose(fp);
290
- fclose(out);
291
- return result ? result : strdup("");
292
- }
293
-
294
- // tool registry
295
- Tool tools[] = {
296
- {"shell", tool_shell},
297
- {"read_file", tool_read_file},
298
- {"write_file", tool_write_file},
299
- {"http_get", tool_http_get},
300
- {"math", tool_math},
301
- {NULL, NULL}
302
- };
303
-
304
- char* execute_tool(const char *name, const char *args) {
305
- for (Tool *t = tools; t->name; t++) {
306
- if (strcmp(t->name, name) == 0) {
307
- return t->func(args);
308
- }
309
- }
310
- return strdup("error: unknown tool");
311
- }
312
-
313
- // --------------------------------------------------------------------
314
- // LLM interaction (Ollama)
315
- // --------------------------------------------------------------------
316
- typedef struct {
317
- char *data;
318
- size_t len;
319
- } ResponseBuffer;
320
-
321
- size_t write_response(void *ptr, size_t size, size_t nmemb, void *stream) {
322
- ResponseBuffer *buf = (ResponseBuffer*)stream;
323
- size_t total = size * nmemb;
324
- buf->data = realloc(buf->data, buf->len + total + 1);
325
- if (!buf->data) return 0;
326
- memcpy(buf->data + buf->len, ptr, total);
327
- buf->len += total;
328
- buf->data[buf->len] = '\0';
329
- return total;
330
- }
331
-
332
- // call Ollama generate endpoint, return JSON string (malloced)
333
- char* ollama_generate(const char *prompt, const char *model, const char *endpoint) {
334
- CURL *curl = curl_easy_init();
335
- if (!curl) return NULL;
336
-
337
- char url[256];
338
- snprintf(url, sizeof(url), "%s/api/generate", endpoint);
339
-
340
- cJSON *req_json = cJSON_CreateObject();
341
- cJSON_AddStringToObject(req_json, "model", model);
342
- cJSON_AddStringToObject(req_json, "prompt", prompt);
343
- cJSON_AddBoolToObject(req_json, "stream", false);
344
- char *req_str = cJSON_PrintUnformatted(req_json);
345
- cJSON_Delete(req_json);
346
-
347
- struct curl_slist *headers = NULL;
348
- headers = curl_slist_append(headers, "Content-Type: application/json");
349
-
350
- ResponseBuffer resp = {0};
351
- curl_easy_setopt(curl, CURLOPT_URL, url);
352
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
353
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
354
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req_str);
355
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_response);
356
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
357
-
358
- CURLcode res = curl_easy_perform(curl);
359
- free(req_str);
360
- curl_slist_free_all(headers);
361
- curl_easy_cleanup(curl);
362
-
363
- if (res != CURLE_OK) {
364
- free(resp.data);
365
- return NULL;
366
- }
367
- return resp.data; // caller must free
368
- }
369
-
370
- // --------------------------------------------------------------------
371
- // Prompt builder (very simple: system + last 5 user/assistant messages)
372
- // --------------------------------------------------------------------
373
- typedef struct {
374
- char *text;
375
- size_t cap;
376
- size_t len;
377
- } StringBuilder;
378
-
379
- void sb_append(StringBuilder *sb, const char *s) {
380
- size_t add = strlen(s);
381
- if (sb->len + add + 1 > sb->cap) {
382
- sb->cap = sb->cap ? sb->cap * 2 : 1024;
383
- while (sb->len + add + 1 > sb->cap) sb->cap *= 2;
384
- sb->text = realloc(sb->text, sb->cap);
385
- }
386
- memcpy(sb->text + sb->len, s, add);
387
- sb->len += add;
388
- sb->text[sb->len] = '\0';
389
- }
390
-
391
- void collect_blob(const BlobHeader *bh, const char *payload, void *user) {
392
- StringBuilder *sb = (StringBuilder*)user;
393
- static int count = 0;
394
- // keep only system (kind 1) and last 10 user/assistant/tool messages
395
- if (bh->kind == 1) {
396
- sb_append(sb, "[System]\n");
397
- sb_append(sb, payload);
398
- sb_append(sb, "\n\n");
399
- } else if (bh->kind == 2 || bh->kind == 3 || bh->kind == 5) {
400
- if (count < 10) {
401
- const char *role = bh->kind==2 ? "User" : (bh->kind==3 ? "Assistant" : "Tool");
402
- sb_append(sb, "[");
403
- sb_append(sb, role);
404
- sb_append(sb, "]\n");
405
- sb_append(sb, payload);
406
- sb_append(sb, "\n\n");
407
- count++;
408
- }
409
- }
410
- }
411
-
412
- char* build_prompt(ShadowArena *arena) {
413
- StringBuilder sb = {0};
414
- // include system prompt and recent conversation
415
- blob_foreach(arena, collect_blob, &sb);
416
- // add instructions for tool use
417
- sb_append(&sb,
418
- "[Instruction]\n"
419
- "You are ShadowClaw, a tiny AI agent. You can use tools by outputting a JSON block like:\n"
420
- "```tool\n{\"tool\":\"name\",\"args\":\"arguments\"}\n```\n"
421
- "Available tools: shell, read_file, write_file, http_get, math.\n"
422
- "After using a tool, you'll see its result. Continue the conversation.\n\n"
423
- "[User]\n");
424
- return sb.text; // caller must free
425
- }
426
-
427
- // --------------------------------------------------------------------
428
- // Parse tool call from assistant text (look for ```tool ... ```)
429
- // --------------------------------------------------------------------
430
- char* parse_tool_call(const char *text, char **tool_name, char **tool_args) {
431
- const char *start = strstr(text, "```tool");
432
- if (!start) return NULL;
433
- start += 7; // skip ```tool
434
- while (*start == ' ' || *start == '\n') start++;
435
- const char *end = strstr(start, "```");
436
- if (!end) return NULL;
437
-
438
- size_t len = end - start;
439
- char *json_str = malloc(len + 1);
440
- memcpy(json_str, start, len);
441
- json_str[len] = '\0';
442
-
443
- cJSON *root = cJSON_Parse(json_str);
444
- free(json_str);
445
- if (!root) return NULL;
446
-
447
- cJSON *name = cJSON_GetObjectItem(root, "tool");
448
- cJSON *args = cJSON_GetObjectItem(root, "args");
449
- if (!cJSON_IsString(name) || !cJSON_IsString(args)) {
450
- cJSON_Delete(root);
451
- return NULL;
452
- }
453
-
454
- *tool_name = strdup(name->valuestring);
455
- *tool_args = strdup(args->valuestring);
456
- cJSON_Delete(root);
457
- return (char*)end + 3; // pointer after the closing ```
458
- }
459
-
460
- // --------------------------------------------------------------------
461
- // Main
462
- // --------------------------------------------------------------------
463
- int main(int argc, char **argv) {
464
- const char *state_file = "shadowclaw.bin";
465
- const char *ollama_endpoint = "http://localhost:11434";
466
- const char *ollama_model = "llama3.2"; // change as needed
467
-
468
- ShadowArena arena = shadow_arena_create(128 * 1024); // 128KB start
469
-
470
- // load previous state if exists
471
- if (access(state_file, F_OK) == 0) {
472
- if (arena_load(&arena, state_file)) {
473
- printf("[ShadowClaw] loaded state from %s\n", state_file);
474
- } else {
475
- printf("[ShadowClaw] failed to load %s, starting fresh\n", state_file);
476
- }
477
- } else {
478
- // bootstrap system prompt
479
- const char *sys = "You are ShadowClaw – tiny, shadowy, Unix‑punk AI agent. Use tools when helpful. Stay minimal.";
480
- blob_append(&arena, 1, 1, sys, strlen(sys)+1);
481
- }
482
-
483
- uint64_t msg_id = time(NULL); // simple id
484
-
485
- printf("ShadowClaw ready. Type your message (Ctrl-D to exit)\n");
486
- char line[4096];
487
- while (fgets(line, sizeof(line), stdin)) {
488
- // remove trailing newline
489
- line[strcspn(line, "\n")] = 0;
490
- if (strlen(line) == 0) continue;
491
-
492
- // store user message
493
- blob_append(&arena, 2, msg_id++, line, strlen(line)+1);
494
-
495
- // build prompt from arena
496
- char *prompt = build_prompt(&arena);
497
- if (!prompt) {
498
- fprintf(stderr, "error building prompt\n");
499
- break;
500
- }
501
-
502
- // append current user input (already in prompt builder? we added it separately)
503
- // but we already appended to arena, and builder collected last 10 messages,
504
- // so the new user message will be included. No need to add again.
505
-
506
- // call ollama
507
- char *response_json = ollama_generate(prompt, ollama_model, ollama_endpoint);
508
- free(prompt);
509
- if (!response_json) {
510
- fprintf(stderr, "LLM call failed\n");
511
- break;
512
- }
513
-
514
- // parse response
515
- cJSON *root = cJSON_Parse(response_json);
516
- free(response_json);
517
- if (!root) {
518
- fprintf(stderr, "JSON parse error\n");
519
- break;
520
- }
521
- cJSON *resp_text = cJSON_GetObjectItem(root, "response");
522
- if (!cJSON_IsString(resp_text)) {
523
- cJSON_Delete(root);
524
- fprintf(stderr, "no 'response' field\n");
525
- break;
526
- }
527
- const char *assistant_msg = resp_text->valuestring;
528
-
529
- // check for tool call
530
- char *tool_name = NULL, *tool_args = NULL;
531
- char *after_tool = parse_tool_call(assistant_msg, &tool_name, &tool_args);
532
- if (tool_name && tool_args) {
533
- // execute tool
534
- char *tool_result = execute_tool(tool_name, tool_args);
535
- // store tool call and result
536
- blob_append(&arena, 4, msg_id++, assistant_msg, after_tool - assistant_msg);
537
- blob_append(&arena, 5, msg_id++, tool_result, strlen(tool_result)+1);
538
- // print result and continue (LLM will see it in next round)
539
- printf("\n[Tool %s] → %s\n", tool_name, tool_result);
540
- free(tool_result);
541
- free(tool_name);
542
- free(tool_args);
543
- } else {
544
- // normal assistant response
545
- printf("\n[ShadowClaw] %s\n", assistant_msg);
546
- blob_append(&arena, 3, msg_id++, assistant_msg, strlen(assistant_msg)+1);
547
- }
548
-
549
- cJSON_Delete(root);
550
-
551
- // save arena after each interaction (dirty flag is set)
552
- if (!arena_save(&arena, state_file)) {
553
- fprintf(stderr, "warning: could not save state\n");
554
- }
555
- }
556
-
557
- shadow_arena_destroy(&arena);
558
- return 0;
559
- }