#include "cli/cli.h" #include "mcp/tools/common/common.h" #include "storage/codec.h" #include "storage/store.h" #include "sys/system.h" #include #include #include static bool export_finish(FILE *output, const char *temporary_path, const char *destination) { bool ok = fflush(output) == 0; if (output == stdout) { return ok; } if (fclose(output) != 0) { ok = false; } if (ok && !tiny_sys_path_replace(temporary_path, destination)) { ok = false; } if (!ok) { (void)tiny_sys_file_remove(temporary_path); } return ok; } int tiny_cli_export(tiny_store_t *store, const tiny_cli_options_t *options) { size_t index; size_t emitted = 0; FILE *output = NULL; char *temporary_path = NULL; tiny_json_buffer_t item; if (!options || !options->export_path) { fprintf(stderr, "export: path is required\n"); return 1; } if (strcmp(options->export_path, "-") == 0) { output = stdout; } else { size_t path_length = strlen(options->export_path); temporary_path = malloc(path_length + sizeof(".tmp")); if (!temporary_path) { fprintf(stderr, "export: out of memory\n"); return 1; } memcpy(temporary_path, options->export_path, path_length); memcpy(temporary_path + path_length, ".tmp", sizeof(".tmp")); if (fopen_s(&output, temporary_path, "wb") != 0 || !output) { fprintf(stderr, "export: unable to open '%s'\n", options->export_path); free(temporary_path); return 1; } } if (fprintf(output, "{\"type\":\"tiny-convmem-manifest\",\"format_version\":2}\n") < 0) { fprintf(stderr, "export: failed to write manifest\n"); if (output != stdout) { fclose(output); (void)tiny_sys_file_remove(temporary_path); } free(temporary_path); return 1; } for (index = 0; index < tiny_store_entry_count(store); ++index) { const tiny_store_entry_t *entry = tiny_store_entry(store, index); int write_failed; if (!entry) { continue; } if (entry->deleted && !options->include_deleted) { continue; } if (options->session_id && options->session_id[0] && strcmp(entry->message.session_id, options->session_id) != 0) { continue; } tiny_json_init(&item); if (entry->deleted) { tiny_json_raw(&item, "{\"type\":\"DEL\",\"message_id\":"); tiny_json_string(&item, entry->message.message_id); tiny_json_raw(&item, "}"); } else { tiny_mcp_tool_append_message(&item, &entry->message); } write_failed = item.failed || fprintf(output, "%s\n", item.data ? item.data : "") < 0; tiny_json_free(&item); if (write_failed) { fprintf(stderr, "export: failed to write record\n"); if (output != stdout) { fclose(output); (void)tiny_sys_file_remove(temporary_path); } free(temporary_path); return 1; } emitted++; } if (!export_finish(output, temporary_path, options->export_path)) { fprintf(stderr, "export: unable to complete '%s'\n", options->export_path); free(temporary_path); return 1; } free(temporary_path); (void)emitted; return 0; }