// PUBLIC DOMAIN - NO LICENSE, NO WARRANTY // // This is free public domain software for the public good of a permacomputer hosted // at permacomputer.com - an always-on computer by the people, for the people. One // which is durable, easy to repair, and distributed like tap water for machine // learning intelligence. // // The permacomputer is community-owned infrastructure optimized around four values: // // TRUTH - First principles, math & science, open source code freely distributed // FREEDOM - Voluntary partnerships, freedom from tyranny & corporate control // HARMONY - Minimal waste, self-renewing systems with diverse thriving connections // LOVE - Be yourself without hurting others, cooperation through natural law // // This software contributes to that vision by enabling code execution across 42+ // programming languages through a unified interface, accessible to all. Code is // seeds to sprout on any abandoned technology. // // Learn more: https://www.permacomputer.com // // Anyone is free to copy, modify, publish, use, compile, sell, or distribute this // software, either in source code form or as a compiled binary, for any purpose, // commercial or non-commercial, and by any means. // // NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. // // That said, our permacomputer's digital membrane stratum continuously runs unit, // integration, and functional tests on all of it's own software - with our // permacomputer monitoring itself, repairing itself, with minimal human in the // loop guidance. Our agents do their best. // // Copyright 2025 TimeHexOn & foxhop & russell@unturf // https://www.timehexon.com // https://www.foxhop.net // https://www.unturf.com/software // UN CLI and Library - C++ Implementation (using curl subprocess for simplicity) // Compile: g++ -o un_cpp un.cpp -std=c++17 // // Library Usage (C++): // std::string execute(const std::string& language, const std::string& code, // const std::string& public_key, const std::string& secret_key) // std::string execute_async(const std::string& language, const std::string& code, // const std::string& public_key, const std::string& secret_key) // std::string get_job(const std::string& job_id, // const std::string& public_key, const std::string& secret_key) // std::string wait_for_job(const std::string& job_id, // const std::string& public_key, const std::string& secret_key) // std::string cancel_job(const std::string& job_id, // const std::string& public_key, const std::string& secret_key) // std::string list_jobs(const std::string& public_key, const std::string& secret_key) // std::string get_languages(const std::string& public_key, const std::string& secret_key) // std::string detect_language(const std::string& filename) // // CLI Usage: // un_cpp script.py // un_cpp -e KEY=VALUE -f data.txt script.py // un_cpp session --list // un_cpp service --name web --ports 8080 #include #include #include #include #include #include #include #include #include using namespace std; const string API_BASE = "https://api.unsandbox.com"; const string PORTAL_BASE = "https://unsandbox.com"; const int LANGUAGES_CACHE_TTL = 3600; // 1 hour in seconds const string BLUE = "\033[34m"; const string RED = "\033[31m"; const string GREEN = "\033[32m"; const string YELLOW = "\033[33m"; const string RESET = "\033[0m"; map lang_map = { {".py", "python"}, {".js", "javascript"}, {".ts", "typescript"}, {".rb", "ruby"}, {".php", "php"}, {".pl", "perl"}, {".lua", "lua"}, {".sh", "bash"}, {".go", "go"}, {".rs", "rust"}, {".c", "c"}, {".cpp", "cpp"}, {".cc", "cpp"}, {".d", "d"}, {".zig", "zig"}, {".nim", "nim"}, {".v", "v"} }; string detect_language(const string& filename) { size_t dot = filename.rfind('.'); if (dot == string::npos) return ""; string ext = filename.substr(dot); return (lang_map.count(ext)) ? lang_map[ext] : ""; } string read_file(const string& filename) { ifstream f(filename); stringstream buf; buf << f.rdbuf(); return buf.str(); } string escape_json(const string& s) { ostringstream o; for (char c : s) { switch (c) { case '"': o << "\\\""; break; case '\\': o << "\\\\"; break; case '\n': o << "\\n"; break; case '\r': o << "\\r"; break; case '\t': o << "\\t"; break; default: o << c; break; } } return o.str(); } // Base64 encoding static const char b64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; string base64_encode(const string& input) { string output; int val = 0, valb = -6; for (unsigned char c : input) { val = (val << 8) + c; valb += 8; while (valb >= 0) { output.push_back(b64_table[(val >> valb) & 0x3F]); valb -= 6; } } if (valb > -6) output.push_back(b64_table[((val << 8) >> (valb + 8)) & 0x3F]); while (output.size() % 4) output.push_back('='); return output; } string exec_curl(const string& cmd) { FILE* pipe = popen(cmd.c_str(), "r"); if (!pipe) return ""; char buffer[4096]; string result; while (fgets(buffer, sizeof(buffer), pipe)) { result += buffer; } pclose(pipe); // Check for timestamp authentication errors if (result.find("timestamp") != string::npos && (result.find("401") != string::npos || result.find("expired") != string::npos || result.find("invalid") != string::npos)) { cerr << RED << "Error: Request timestamp expired (must be within 5 minutes of server time)" << RESET << endl; cerr << YELLOW << "Your computer's clock may have drifted." << RESET << endl; cerr << "Check your system time and sync with NTP if needed:" << endl; cerr << " Linux: sudo ntpdate -s time.nist.gov" << endl; cerr << " macOS: sudo sntp -sS time.apple.com" << endl; cerr << " Windows: w32tm /resync" << endl; exit(1); } return result; } string compute_hmac(const string& secret_key, const string& message) { string cmd = "echo -n '" + message + "' | openssl dgst -sha256 -hmac '" + secret_key + "' -hex | sed 's/.*= //'"; string result = exec_curl(cmd); // Trim newline while (!result.empty() && (result.back() == '\n' || result.back() == '\r')) { result.pop_back(); } return result; } string get_timestamp() { return to_string(time(nullptr)); } string build_auth_headers(const string& method, const string& path, const string& body, const string& public_key, const string& secret_key) { if (secret_key.empty()) { // Legacy mode: use public_key as bearer token return "-H 'Authorization: Bearer " + public_key + "'"; } // HMAC mode string timestamp = get_timestamp(); string message = timestamp + ":" + method + ":" + path + ":" + body; string signature = compute_hmac(secret_key, message); return "-H 'Authorization: Bearer " + public_key + "' " "-H 'X-Timestamp: " + timestamp + "' " "-H 'X-Signature: " + signature + "'"; } string build_env_content(const vector& envs, const string& env_file) { ostringstream parts; if (!env_file.empty()) { string content = read_file(env_file); // Trim trailing whitespace while (!content.empty() && (content.back() == '\n' || content.back() == '\r' || content.back() == ' ')) { content.pop_back(); } parts << content; } for (const auto& e : envs) { if (e.find('=') != string::npos) { if (parts.str().length() > 0) parts << "\n"; parts << e; } } return parts.str(); } string get_languages_cache_path() { const char* home = getenv("HOME"); if (!home) home = "."; return string(home) + "/.unsandbox/languages.json"; } vector load_languages_cache() { vector empty; string cache_path = get_languages_cache_path(); struct stat st; if (stat(cache_path.c_str(), &st) != 0) { return empty; // File doesn't exist } // Check if cache is fresh (< 1 hour old) time_t now = time(nullptr); if (now - st.st_mtime >= LANGUAGES_CACHE_TTL) { return empty; // Cache expired } string content = read_file(cache_path); if (content.empty()) { return empty; } // Parse languages from JSON {"languages": [...], "timestamp": ...} vector languages; size_t pos = content.find("\"languages\":"); if (pos == string::npos) return empty; pos = content.find('[', pos); if (pos == string::npos) return empty; pos++; while (pos < content.length()) { // Skip whitespace while (pos < content.length() && (content[pos] == ' ' || content[pos] == '\n' || content[pos] == '\t')) pos++; if (content[pos] == ']') break; if (content[pos] == '"') { pos++; size_t end = content.find('"', pos); if (end != string::npos) { languages.push_back(content.substr(pos, end - pos)); pos = end + 1; } } // Skip comma while (pos < content.length() && (content[pos] == ',' || content[pos] == ' ' || content[pos] == '\n' || content[pos] == '\t')) pos++; } return languages; } void save_languages_cache(const vector& languages) { string cache_path = get_languages_cache_path(); // Create directory if needed string dir = cache_path.substr(0, cache_path.rfind('/')); mkdir(dir.c_str(), 0755); ofstream f(cache_path); if (!f) return; f << "{\"languages\":["; for (size_t i = 0; i < languages.size(); i++) { if (i > 0) f << ","; f << "\"" << languages[i] << "\""; } f << "],\"timestamp\":" << time(nullptr) << "}"; f.close(); } string service_env_status(const string& service_id, const string& public_key, const string& secret_key) { string path = "/services/" + service_id + "/env"; string auth_headers = build_auth_headers("GET", path, "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + path + "' " + auth_headers; return exec_curl(cmd); } bool service_env_set(const string& service_id, const string& env_content, const string& public_key, const string& secret_key) { string path = "/services/" + service_id + "/env"; string timestamp = get_timestamp(); string message = timestamp + ":PUT:" + path + ":" + env_content; string signature = compute_hmac(secret_key, message); string cmd = "curl -s -X PUT '" + API_BASE + path + "' " "-H 'Content-Type: text/plain' " "-H 'Authorization: Bearer " + public_key + "' " "-H 'X-Timestamp: " + timestamp + "' " "-H 'X-Signature: " + signature + "' " "-d '" + env_content + "'"; exec_curl(cmd); return true; } string service_env_export(const string& service_id, const string& public_key, const string& secret_key) { string path = "/services/" + service_id + "/env/export"; string auth_headers = build_auth_headers("POST", path, "", public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " + auth_headers; return exec_curl(cmd); } bool service_env_delete(const string& service_id, const string& public_key, const string& secret_key) { string path = "/services/" + service_id + "/env"; string auth_headers = build_auth_headers("DELETE", path, "", public_key, secret_key); string cmd = "curl -s -X DELETE '" + API_BASE + path + "' " + auth_headers; exec_curl(cmd); return true; } void set_unfreeze_on_demand(const string& service_id, bool enabled, const string& public_key, const string& secret_key) { string path = "/services/" + service_id; string enabled_str = enabled ? "true" : "false"; string body = "{\"unfreeze_on_demand\":" + enabled_str + "}"; string auth_headers = build_auth_headers("PATCH", path, body, public_key, secret_key); string cmd = "curl -s -X PATCH '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + body + "'"; exec_curl(cmd); cout << GREEN << "Service unfreeze_on_demand set to " << enabled_str << RESET << endl; } void cmd_service_env(const string& action, const string& target, const vector& envs, const string& env_file, const string& public_key, const string& secret_key) { if (action == "status") { if (target.empty()) { cerr << RED << "Error: Usage: service env status " << RESET << endl; exit(1); } string result = service_env_status(target, public_key, secret_key); bool has_env = result.find("\"has_env\":true") != string::npos; cout << "Service: " << target << endl; cout << "Has Vault: " << (has_env ? "Yes" : "No") << endl; if (has_env) { size_t size_pos = result.find("\"size\":"); if (size_pos != string::npos) { size_pos += 7; size_t end = result.find_first_not_of("0123456789", size_pos); cout << "Size: " << result.substr(size_pos, end - size_pos) << " bytes" << endl; } size_t updated_pos = result.find("\"updated_at\":\""); if (updated_pos != string::npos) { updated_pos += 14; size_t end = result.find("\"", updated_pos); cout << "Updated: " << result.substr(updated_pos, end - updated_pos) << endl; } } } else if (action == "set") { if (target.empty()) { cerr << RED << "Error: Usage: service env set [-e KEY=VAL] [--env-file FILE]" << RESET << endl; exit(1); } string env_content = build_env_content(envs, env_file); if (env_content.empty()) { cerr << RED << "Error: No environment variables specified. Use -e KEY=VAL or --env-file FILE" << RESET << endl; exit(1); } if (env_content.length() > 65536) { cerr << RED << "Error: Environment content exceeds 64KB limit" << RESET << endl; exit(1); } service_env_set(target, env_content, public_key, secret_key); cout << GREEN << "Vault updated for service: " << target << RESET << endl; } else if (action == "export") { if (target.empty()) { cerr << RED << "Error: Usage: service env export " << RESET << endl; exit(1); } string result = service_env_export(target, public_key, secret_key); size_t content_pos = result.find("\"content\":\""); if (content_pos != string::npos) { content_pos += 11; size_t end = result.find("\"", content_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string content = result.substr(content_pos, end - content_pos); // Unescape size_t pos = 0; while ((pos = content.find("\\n", pos)) != string::npos) { content.replace(pos, 2, "\n"); } cout << content; if (!content.empty() && content.back() != '\n') cout << endl; } } else { cerr << YELLOW << "Vault is empty" << RESET << endl; } } else if (action == "delete") { if (target.empty()) { cerr << RED << "Error: Usage: service env delete " << RESET << endl; exit(1); } service_env_delete(target, public_key, secret_key); cout << GREEN << "Vault deleted for service: " << target << RESET << endl; } else { cerr << RED << "Error: Unknown env action: " << action << ". Use status, set, export, or delete" << RESET << endl; exit(1); } } void cmd_execute(const string& source_file, const vector& envs, const vector& files, bool artifacts, const string& network, int vcpu, const string& public_key, const string& secret_key) { string lang = detect_language(source_file); if (lang.empty()) { cerr << RED << "Error: Cannot detect language" << RESET << endl; exit(1); } string code = read_file(source_file); ostringstream json; json << "{\"language\":\"" << lang << "\",\"code\":\"" << escape_json(code) << "\""; if (!envs.empty()) { json << ",\"env\":{"; for (size_t i = 0; i < envs.size(); i++) { size_t eq = envs[i].find('='); if (eq != string::npos) { if (i > 0) json << ","; json << "\"" << envs[i].substr(0, eq) << "\":\"" << escape_json(envs[i].substr(eq + 1)) << "\""; } } json << "}"; } if (artifacts) json << ",\"return_artifacts\":true"; if (!network.empty()) json << ",\"network\":\"" << network << "\""; if (vcpu > 0) json << ",\"vcpu\":" << vcpu; json << "}"; string auth_headers = build_auth_headers("POST", "/execute", json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/execute' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json.str() + "'"; string result = exec_curl(cmd); // Simple parsing (stdout/stderr/exit_code) size_t stdout_pos = result.find("\"stdout\":\""); size_t stderr_pos = result.find("\"stderr\":\""); size_t exit_pos = result.find("\"exit_code\":"); if (stdout_pos != string::npos) { stdout_pos += 10; size_t end = result.find("\"", stdout_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string out = result.substr(stdout_pos, end - stdout_pos); // Unescape size_t pos = 0; while ((pos = out.find("\\n", pos)) != string::npos) { out.replace(pos, 2, "\n"); } cout << BLUE << out << RESET; } } if (stderr_pos != string::npos) { stderr_pos += 10; size_t end = result.find("\"", stderr_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string err = result.substr(stderr_pos, end - stderr_pos); size_t pos = 0; while ((pos = err.find("\\n", pos)) != string::npos) { err.replace(pos, 2, "\n"); } cerr << RED << err << RESET; } } int exit_code = 1; if (exit_pos != string::npos) { exit_code = stoi(result.substr(exit_pos + 12)); } exit(exit_code); } void cmd_session(bool list, const string& kill, const string& shell, const string& network, int vcpu, bool tmux, bool screen, const vector& files, const string& public_key, const string& secret_key) { if (list) { string auth_headers = build_auth_headers("GET", "/sessions", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/sessions' " + auth_headers; cout << exec_curl(cmd) << endl; return; } if (!kill.empty()) { string auth_headers = build_auth_headers("DELETE", "/sessions/" + kill, "", public_key, secret_key); string cmd = "curl -s -X DELETE '" + API_BASE + "/sessions/" + kill + "' " + auth_headers; exec_curl(cmd); cout << GREEN << "Session terminated: " << kill << RESET << endl; return; } ostringstream json; json << "{\"shell\":\"" << (shell.empty() ? "bash" : shell) << "\""; if (!network.empty()) json << ",\"network\":\"" << network << "\""; if (vcpu > 0) json << ",\"vcpu\":" << vcpu; if (tmux) json << ",\"persistence\":\"tmux\""; if (screen) json << ",\"persistence\":\"screen\""; // Input files if (!files.empty()) { json << ",\"input_files\":["; for (size_t i = 0; i < files.size(); i++) { if (i > 0) json << ","; ifstream file(files[i], ios::binary); if (!file) { cerr << RED << "Error: Input file not found: " << files[i] << RESET << endl; exit(1); } ostringstream content; content << file.rdbuf(); string b64 = base64_encode(content.str()); string filename = files[i].substr(files[i].find_last_of("/\\") + 1); json << "{\"filename\":\"" << filename << "\",\"content_base64\":\"" << b64 << "\"}"; } json << "]"; } json << "}"; cout << YELLOW << "Creating session..." << RESET << endl; string auth_headers = build_auth_headers("POST", "/sessions", json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/sessions' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json.str() + "'"; cout << exec_curl(cmd) << endl; } void cmd_service(const string& name, const string& ports, const string& type, const string& bootstrap, const string& bootstrap_file, const vector& files, bool list, const string& info, const string& logs, const string& tail, const string& sleep, const string& wake, const string& destroy, const string& resize, const string& execute, const string& command, const string& dump_bootstrap, const string& dump_file, const string& network, int vcpu, const vector& envs, const string& env_file, const string& env_action, const string& env_target, const string& set_unfreeze_on_demand_id, int set_unfreeze_on_demand_enabled, int unfreeze_on_demand, const string& public_key, const string& secret_key) { // Handle service env subcommand if (!env_action.empty()) { cmd_service_env(env_action, env_target, envs, env_file, public_key, secret_key); return; } if (list) { string auth_headers = build_auth_headers("GET", "/services", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/services' " + auth_headers; cout << exec_curl(cmd) << endl; return; } if (!info.empty()) { string auth_headers = build_auth_headers("GET", "/services/" + info, "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/services/" + info + "' " + auth_headers; cout << exec_curl(cmd) << endl; return; } if (!logs.empty()) { string auth_headers = build_auth_headers("GET", "/services/" + logs + "/logs", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/services/" + logs + "/logs' " + auth_headers; exec_curl(cmd); return; } if (!tail.empty()) { string auth_headers = build_auth_headers("GET", "/services/" + tail + "/logs?lines=9000", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/services/" + tail + "/logs?lines=9000' " + auth_headers; exec_curl(cmd); return; } if (!sleep.empty()) { string auth_headers = build_auth_headers("POST", "/services/" + sleep + "/freeze", "", public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/services/" + sleep + "/freeze' " + auth_headers; exec_curl(cmd); cout << GREEN << "Service frozen: " << sleep << RESET << endl; return; } if (!wake.empty()) { string auth_headers = build_auth_headers("POST", "/services/" + wake + "/unfreeze", "", public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/services/" + wake + "/unfreeze' " + auth_headers; exec_curl(cmd); cout << GREEN << "Service unfreezing: " << wake << RESET << endl; return; } if (!destroy.empty()) { string auth_headers = build_auth_headers("DELETE", "/services/" + destroy, "", public_key, secret_key); string cmd = "curl -s -X DELETE '" + API_BASE + "/services/" + destroy + "' " + auth_headers; exec_curl(cmd); cout << GREEN << "Service destroyed: " << destroy << RESET << endl; return; } if (!resize.empty()) { if (vcpu <= 0) { cerr << RED << "Error: --resize requires -v " << RESET << endl; exit(1); } ostringstream json; json << "{\"vcpu\":" << vcpu << "}"; string auth_headers = build_auth_headers("PATCH", "/services/" + resize, json.str(), public_key, secret_key); string cmd = "curl -s -X PATCH '" + API_BASE + "/services/" + resize + "' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json.str() + "'"; exec_curl(cmd); cout << GREEN << "Service resized to " << vcpu << " vCPU, " << (vcpu * 2) << " GB RAM" << RESET << endl; return; } if (!set_unfreeze_on_demand_id.empty()) { if (set_unfreeze_on_demand_enabled < 0) { cerr << RED << "Error: --set-unfreeze-on-demand requires --enabled true|false" << RESET << endl; exit(1); } set_unfreeze_on_demand(set_unfreeze_on_demand_id, set_unfreeze_on_demand_enabled == 1, public_key, secret_key); return; } if (!execute.empty()) { ostringstream json; json << "{\"command\":\"" << escape_json(command) << "\"}"; string auth_headers = build_auth_headers("POST", "/services/" + execute + "/execute", json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/services/" + execute + "/execute' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json.str() + "'"; string result = exec_curl(cmd); size_t stdout_pos = result.find("\"stdout\":\""); size_t stderr_pos = result.find("\"stderr\":\""); if (stdout_pos != string::npos) { stdout_pos += 10; size_t end = result.find("\"", stdout_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string out = result.substr(stdout_pos, end - stdout_pos); size_t pos = 0; while ((pos = out.find("\\n", pos)) != string::npos) { out.replace(pos, 2, "\n"); } cout << BLUE << out << RESET; } } if (stderr_pos != string::npos) { stderr_pos += 10; size_t end = result.find("\"", stderr_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string err = result.substr(stderr_pos, end - stderr_pos); size_t pos = 0; while ((pos = err.find("\\n", pos)) != string::npos) { err.replace(pos, 2, "\n"); } cerr << RED << err << RESET; } } return; } if (!dump_bootstrap.empty()) { cerr << "Fetching bootstrap script from " << dump_bootstrap << "..." << endl; string json_body = "{\"command\":\"cat /tmp/bootstrap.sh\"}"; string auth_headers = build_auth_headers("POST", "/services/" + dump_bootstrap + "/execute", json_body, public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/services/" + dump_bootstrap + "/execute' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json_body + "'"; string result = exec_curl(cmd); size_t stdout_pos = result.find("\"stdout\":\""); if (stdout_pos != string::npos) { stdout_pos += 10; size_t end = result.find("\"", stdout_pos); while (end > 0 && result[end-1] == '\\') end = result.find("\"", end+1); if (end != string::npos) { string bootstrap_script = result.substr(stdout_pos, end - stdout_pos); size_t pos = 0; while ((pos = bootstrap_script.find("\\n", pos)) != string::npos) { bootstrap_script.replace(pos, 2, "\n"); } if (!dump_file.empty()) { ofstream outfile(dump_file); if (outfile) { outfile << bootstrap_script; outfile.close(); chmod(dump_file.c_str(), 0755); cout << "Bootstrap saved to " << dump_file << endl; } else { cerr << RED << "Error: Could not write to " << dump_file << RESET << endl; exit(1); } } else { cout << bootstrap_script; } } else { cerr << RED << "Error: Failed to fetch bootstrap (service not running or no bootstrap file)" << RESET << endl; exit(1); } } else { cerr << RED << "Error: Failed to fetch bootstrap (service not running or no bootstrap file)" << RESET << endl; exit(1); } return; } if (!name.empty()) { ostringstream json; json << "{\"name\":\"" << name << "\""; if (!ports.empty()) json << ",\"ports\":[" << ports << "]"; if (!type.empty()) json << ",\"service_type\":\"" << type << "\""; if (!bootstrap.empty()) { json << ",\"bootstrap\":\"" << escape_json(bootstrap) << "\""; } if (!bootstrap_file.empty()) { struct stat st; if (stat(bootstrap_file.c_str(), &st) == 0) { string boot_code = read_file(bootstrap_file); json << ",\"bootstrap_content\":\"" << escape_json(boot_code) << "\""; } else { cerr << RED << "Error: Bootstrap file not found: " << bootstrap_file << RESET << endl; exit(1); } } // Input files if (!files.empty()) { json << ",\"input_files\":["; for (size_t i = 0; i < files.size(); i++) { if (i > 0) json << ","; ifstream file(files[i], ios::binary); if (!file) { cerr << RED << "Error: Input file not found: " << files[i] << RESET << endl; exit(1); } ostringstream content; content << file.rdbuf(); string b64 = base64_encode(content.str()); string filename = files[i].substr(files[i].find_last_of("/\\") + 1); json << "{\"filename\":\"" << filename << "\",\"content_base64\":\"" << b64 << "\"}"; } json << "]"; } if (!network.empty()) json << ",\"network\":\"" << network << "\""; if (vcpu > 0) json << ",\"vcpu\":" << vcpu; if (unfreeze_on_demand >= 0) json << ",\"unfreeze_on_demand\":" << (unfreeze_on_demand ? "true" : "false"); json << "}"; cout << YELLOW << "Creating service..." << RESET << endl; string auth_headers = build_auth_headers("POST", "/services", json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + "/services' " "-H 'Content-Type: application/json' " + auth_headers + " " "-d '" + json.str() + "'"; string result = exec_curl(cmd); cout << result << endl; // Auto-set vault if env vars provided if (!envs.empty() || !env_file.empty()) { // Extract service ID from result size_t id_pos = result.find("\"id\":\""); if (id_pos != string::npos) { id_pos += 6; size_t id_end = result.find("\"", id_pos); if (id_end != string::npos) { string service_id = result.substr(id_pos, id_end - id_pos); string env_content = build_env_content(envs, env_file); if (!env_content.empty() && env_content.length() <= 65536) { service_env_set(service_id, env_content, public_key, secret_key); cout << GREEN << "Vault configured with environment variables" << RESET << endl; } } } } return; } cerr << RED << "Error: Specify --name to create a service" << RESET << endl; exit(1); } void cmd_languages(bool json_output, const string& public_key, const string& secret_key) { // Try cache first vector names = load_languages_cache(); if (names.empty()) { // Fetch from API string auth_headers = build_auth_headers("GET", "/languages", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/languages' " + auth_headers; string result = exec_curl(cmd); // Extract language names from response size_t pos = 0; string search = "\"name\":\""; while ((pos = result.find(search, pos)) != string::npos) { pos += search.length(); size_t end = result.find("\"", pos); if (end != string::npos) { names.push_back(result.substr(pos, end - pos)); pos = end; } } // Save to cache if (!names.empty()) { save_languages_cache(names); } } if (json_output) { cout << "["; for (size_t i = 0; i < names.size(); i++) { if (i > 0) cout << ","; cout << "\"" << names[i] << "\""; } cout << "]" << endl; } else { // Output one language per line for (const auto& name : names) { cout << name << endl; } } } void cmd_image(bool list, const string& info, const string& del, const string& lock, const string& unlock, const string& publish, const string& source_type, const string& visibility_id, const string& visibility, const string& spawn, const string& clone, const string& name, const string& ports, const string& public_key, const string& secret_key) { if (list) { string auth_headers = build_auth_headers("GET", "/images", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/images' " + auth_headers; cout << exec_curl(cmd) << endl; return; } if (!info.empty()) { string path = "/images/" + info; string auth_headers = build_auth_headers("GET", path, "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + path + "' " + auth_headers; cout << exec_curl(cmd) << endl; return; } if (!del.empty()) { string path = "/images/" + del; string auth_headers = build_auth_headers("DELETE", path, "", public_key, secret_key); string cmd = "curl -s -X DELETE '" + API_BASE + path + "' " + auth_headers; exec_curl(cmd); cout << GREEN << "Image deleted: " << del << RESET << endl; return; } if (!lock.empty()) { string path = "/images/" + lock + "/lock"; string body = "{}"; string auth_headers = build_auth_headers("POST", path, body, public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + body + "'"; exec_curl(cmd); cout << GREEN << "Image locked: " << lock << RESET << endl; return; } if (!unlock.empty()) { string path = "/images/" + unlock + "/unlock"; string body = "{}"; string auth_headers = build_auth_headers("POST", path, body, public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + body + "'"; exec_curl(cmd); cout << GREEN << "Image unlocked: " << unlock << RESET << endl; return; } if (!publish.empty()) { if (source_type.empty()) { cerr << RED << "Error: --publish requires --source-type (service or snapshot)" << RESET << endl; exit(1); } ostringstream json; json << "{\"source_type\":\"" << source_type << "\",\"source_id\":\"" << publish << "\""; if (!name.empty()) { json << ",\"name\":\"" << escape_json(name) << "\""; } json << "}"; string path = "/images/publish"; string auth_headers = build_auth_headers("POST", path, json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + json.str() + "'"; cout << GREEN << "Image published" << RESET << endl; cout << exec_curl(cmd) << endl; return; } if (!visibility_id.empty()) { if (visibility.empty()) { cerr << RED << "Error: --visibility requires visibility mode (private, unlisted, public)" << RESET << endl; exit(1); } ostringstream json; json << "{\"visibility\":\"" << visibility << "\"}"; string path = "/images/" + visibility_id + "/visibility"; string auth_headers = build_auth_headers("POST", path, json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + json.str() + "'"; exec_curl(cmd); cout << GREEN << "Image visibility set to: " << visibility << RESET << endl; return; } if (!spawn.empty()) { ostringstream json; json << "{"; bool has_field = false; if (!name.empty()) { json << "\"name\":\"" << escape_json(name) << "\""; has_field = true; } if (!ports.empty()) { if (has_field) json << ","; json << "\"ports\":[" << ports << "]"; } json << "}"; string path = "/images/" + spawn + "/spawn"; string auth_headers = build_auth_headers("POST", path, json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + json.str() + "'"; cout << GREEN << "Service spawned from image" << RESET << endl; cout << exec_curl(cmd) << endl; return; } if (!clone.empty()) { ostringstream json; json << "{"; if (!name.empty()) { json << "\"name\":\"" << escape_json(name) << "\""; } json << "}"; string path = "/images/" + clone + "/clone"; string auth_headers = build_auth_headers("POST", path, json.str(), public_key, secret_key); string cmd = "curl -s -X POST '" + API_BASE + path + "' " "-H 'Content-Type: application/json' " + auth_headers + " -d '" + json.str() + "'"; cout << GREEN << "Image cloned" << RESET << endl; cout << exec_curl(cmd) << endl; return; } // Default: list images string auth_headers = build_auth_headers("GET", "/images", "", public_key, secret_key); string cmd = "curl -s -X GET '" + API_BASE + "/images' " + auth_headers; cout << exec_curl(cmd) << endl; } void cmd_validate_key(bool extend, const string& public_key, const string& secret_key) { string auth_headers = build_auth_headers("POST", "/keys/validate", "", public_key, secret_key); string cmd = "curl -s -X POST '" + PORTAL_BASE + "/keys/validate' " "-H 'Content-Type: application/json' " + auth_headers; string result = exec_curl(cmd); // Parse JSON response size_t status_pos = result.find("\"status\":\""); size_t public_key_pos = result.find("\"public_key\":\""); size_t tier_pos = result.find("\"tier\":\""); size_t expires_pos = result.find("\"expires_at\":\""); if (status_pos == string::npos) { cerr << RED << "Error: Invalid API response" << RESET << endl; exit(1); } // Extract status status_pos += 10; size_t status_end = result.find("\"", status_pos); string status = result.substr(status_pos, status_end - status_pos); // Extract public_key from response string resp_public_key; if (public_key_pos != string::npos) { public_key_pos += 14; size_t pk_end = result.find("\"", public_key_pos); resp_public_key = result.substr(public_key_pos, pk_end - public_key_pos); } // Extract tier string tier; if (tier_pos != string::npos) { tier_pos += 8; size_t tier_end = result.find("\"", tier_pos); tier = result.substr(tier_pos, tier_end - tier_pos); } // Extract expires_at string expires_at; if (expires_pos != string::npos) { expires_pos += 14; size_t expires_end = result.find("\"", expires_pos); expires_at = result.substr(expires_pos, expires_end - expires_pos); } if (status == "valid") { cout << GREEN << "Valid" << RESET << endl; if (!resp_public_key.empty()) { cout << "Public Key: " << resp_public_key << endl; } if (!tier.empty()) { cout << "Tier: " << tier << endl; } if (!expires_at.empty()) { cout << "Expires: " << expires_at << endl; } } else if (status == "expired") { cout << RED << "Expired" << RESET << endl; if (!resp_public_key.empty()) { cout << "Public Key: " << resp_public_key << endl; } if (!tier.empty()) { cout << "Tier: " << tier << endl; } if (!expires_at.empty()) { cout << "Expired: " << expires_at << endl; } cout << YELLOW << "To renew: Visit " << PORTAL_BASE << "/keys/extend" << RESET << endl; if (extend && !resp_public_key.empty()) { string url = PORTAL_BASE + "/keys/extend?pk=" + resp_public_key; string browser_cmd = "xdg-open '" + url + "' 2>/dev/null || open '" + url + "' 2>/dev/null"; system(browser_cmd.c_str()); } } else { cout << RED << "Invalid" << RESET << endl; } } int main(int argc, char* argv[]) { string public_key = getenv("UNSANDBOX_PUBLIC_KEY") ? getenv("UNSANDBOX_PUBLIC_KEY") : ""; string secret_key = getenv("UNSANDBOX_SECRET_KEY") ? getenv("UNSANDBOX_SECRET_KEY") : ""; // Fall back to UNSANDBOX_API_KEY for backwards compatibility if (public_key.empty()) { public_key = getenv("UNSANDBOX_API_KEY") ? getenv("UNSANDBOX_API_KEY") : ""; } if (argc < 2) { cerr << "Usage: " << argv[0] << " [options] " << endl; cerr << " " << argv[0] << " languages [--json]" << endl; cerr << " " << argv[0] << " session [options]" << endl; cerr << " " << argv[0] << " service [options]" << endl; cerr << " " << argv[0] << " image [options]" << endl; cerr << " " << argv[0] << " key [options]" << endl; return 1; } string cmd_type = argv[1]; if (cmd_type == "image") { bool list = false; string info, del, lock, unlock, publish, source_type, visibility_id, visibility; string spawn, clone, name, ports; for (int i = 2; i < argc; i++) { string arg = argv[i]; if (arg == "--list" || arg == "-l") list = true; else if (arg == "--info" && i+1 < argc) info = argv[++i]; else if (arg == "--delete" && i+1 < argc) del = argv[++i]; else if (arg == "--lock" && i+1 < argc) lock = argv[++i]; else if (arg == "--unlock" && i+1 < argc) unlock = argv[++i]; else if (arg == "--publish" && i+1 < argc) publish = argv[++i]; else if (arg == "--source-type" && i+1 < argc) source_type = argv[++i]; else if (arg == "--visibility" && i+2 < argc) { visibility_id = argv[++i]; visibility = argv[++i]; } else if (arg == "--spawn" && i+1 < argc) spawn = argv[++i]; else if (arg == "--clone" && i+1 < argc) clone = argv[++i]; else if (arg == "--name" && i+1 < argc) name = argv[++i]; else if (arg == "--ports" && i+1 < argc) ports = argv[++i]; else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; } cmd_image(list, info, del, lock, unlock, publish, source_type, visibility_id, visibility, spawn, clone, name, ports, public_key, secret_key); return 0; } if (cmd_type == "session") { bool list = false; string kill, shell, network; int vcpu = 0; bool tmux = false, screen = false; vector files; for (int i = 2; i < argc; i++) { string arg = argv[i]; if (arg == "--list") list = true; else if (arg == "--kill" && i+1 < argc) kill = argv[++i]; else if (arg == "--shell" && i+1 < argc) shell = argv[++i]; else if (arg == "-f" && i+1 < argc) files.push_back(argv[++i]); else if (arg == "-n" && i+1 < argc) network = argv[++i]; else if (arg == "-v" && i+1 < argc) vcpu = stoi(argv[++i]); else if (arg == "--tmux") tmux = true; else if (arg == "--screen") screen = true; else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; } cmd_session(list, kill, shell, network, vcpu, tmux, screen, files, public_key, secret_key); return 0; } if (cmd_type == "service") { string name, ports, type, bootstrap, bootstrap_file; bool list = false; string info, logs, tail, sleep, wake, destroy, resize, execute, command, dump_bootstrap, dump_file, network; int vcpu = 0; vector files; vector envs; string env_file, env_action, env_target; string set_unfreeze_on_demand_id; int set_unfreeze_on_demand_enabled = -1; // -1 = not specified, 0 = false, 1 = true int unfreeze_on_demand = -1; // For service creation for (int i = 2; i < argc; i++) { string arg = argv[i]; if (arg == "--name" && i+1 < argc) name = argv[++i]; else if (arg == "--ports" && i+1 < argc) ports = argv[++i]; else if (arg == "--type" && i+1 < argc) type = argv[++i]; else if (arg == "--bootstrap" && i+1 < argc) bootstrap = argv[++i]; else if (arg == "--bootstrap-file" && i+1 < argc) bootstrap_file = argv[++i]; else if (arg == "-f" && i+1 < argc) files.push_back(argv[++i]); else if (arg == "-e" && i+1 < argc) envs.push_back(argv[++i]); else if (arg == "--env-file" && i+1 < argc) env_file = argv[++i]; else if (arg == "env" && env_action.empty()) { // service env if (i+1 < argc && string(argv[i+1])[0] != '-') { env_action = argv[++i]; if (i+1 < argc && string(argv[i+1])[0] != '-') { env_target = argv[++i]; } } } else if (arg == "--list") list = true; else if (arg == "--info" && i+1 < argc) info = argv[++i]; else if (arg == "--logs" && i+1 < argc) logs = argv[++i]; else if (arg == "--tail" && i+1 < argc) tail = argv[++i]; else if (arg == "--freeze" && i+1 < argc) sleep = argv[++i]; else if (arg == "--unfreeze" && i+1 < argc) wake = argv[++i]; else if (arg == "--destroy" && i+1 < argc) destroy = argv[++i]; else if (arg == "--resize" && i+1 < argc) resize = argv[++i]; else if (arg == "--execute" && i+1 < argc) execute = argv[++i]; else if (arg == "--command" && i+1 < argc) command = argv[++i]; else if (arg == "--dump-bootstrap" && i+1 < argc) dump_bootstrap = argv[++i]; else if (arg == "--dump-file" && i+1 < argc) dump_file = argv[++i]; else if (arg == "-n" && i+1 < argc) network = argv[++i]; else if (arg == "-v" && i+1 < argc) vcpu = stoi(argv[++i]); else if (arg == "--set-unfreeze-on-demand" && i+1 < argc) set_unfreeze_on_demand_id = argv[++i]; else if (arg == "--enabled" && i+1 < argc) { string val = argv[++i]; set_unfreeze_on_demand_enabled = (val == "true") ? 1 : 0; } else if (arg == "--unfreeze-on-demand" && i+1 < argc) { string val = argv[++i]; unfreeze_on_demand = (val == "true") ? 1 : 0; } else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; } cmd_service(name, ports, type, bootstrap, bootstrap_file, files, list, info, logs, tail, sleep, wake, destroy, resize, execute, command, dump_bootstrap, dump_file, network, vcpu, envs, env_file, env_action, env_target, set_unfreeze_on_demand_id, set_unfreeze_on_demand_enabled, unfreeze_on_demand, public_key, secret_key); return 0; } if (cmd_type == "key") { bool extend = false; for (int i = 2; i < argc; i++) { string arg = argv[i]; if (arg == "--extend") extend = true; else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; } cmd_validate_key(extend, public_key, secret_key); return 0; } if (cmd_type == "languages") { bool json_output = false; for (int i = 2; i < argc; i++) { string arg = argv[i]; if (arg == "--json") json_output = true; else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; } cmd_languages(json_output, public_key, secret_key); return 0; } // Execute mode vector envs, files; bool artifacts = false; string network, source_file; int vcpu = 0; for (int i = 1; i < argc; i++) { string arg = argv[i]; if (arg == "-e" && i+1 < argc) envs.push_back(argv[++i]); else if (arg == "-f" && i+1 < argc) files.push_back(argv[++i]); else if (arg == "-a") artifacts = true; else if (arg == "-n" && i+1 < argc) network = argv[++i]; else if (arg == "-v" && i+1 < argc) vcpu = stoi(argv[++i]); else if (arg == "-k" && i+1 < argc) public_key = argv[++i]; else if (arg[0] == '-') { cerr << RED << "Unknown option: " << arg << RESET << endl; return 1; } else source_file = arg; } if (source_file.empty()) { cerr << RED << "Error: No source file specified" << RESET << endl; return 1; } cmd_execute(source_file, envs, files, artifacts, network, vcpu, public_key, secret_key); return 0; }