un-inception/un.cpp
russell@unturf.com dc69bf4959 Rename sleep/wake to freeze/unfreeze across all 42 implementations
API endpoint changes:
- /services/{id}/sleep → /services/{id}/freeze
- /services/{id}/wake → /services/{id}/unfreeze

CLI flag changes:
- --sleep → --freeze
- --wake → --unfreeze

Message changes:
- 'Service sleeping' → 'Service frozen'
- 'Service waking' → 'Service unfreezing'
2026-01-15 11:52:15 -05:00

896 lines
36 KiB
C++

// 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 - C++ Implementation (using curl subprocess for simplicity)
// Compile: g++ -o un_cpp un.cpp -std=c++17
// 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 <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cstdlib>
#include <ctime>
#include <sys/stat.h>
using namespace std;
const string API_BASE = "https://api.unsandbox.com";
const string PORTAL_BASE = "https://unsandbox.com";
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<string, string> 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<string>& 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 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 cmd_service_env(const string& action, const string& target, const vector<string>& 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 <service_id>" << 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 <service_id> [-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 <service_id>" << 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 <service_id>" << 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<string>& envs, const vector<string>& 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<string>& 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<string>& 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<string>& envs, const string& env_file, const string& env_action, const string& env_target, 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 <vcpu>" << 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 (!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;
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_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] <source_file>" << endl;
cerr << " " << argv[0] << " session [options]" << endl;
cerr << " " << argv[0] << " service [options]" << endl;
cerr << " " << argv[0] << " key [options]" << endl;
return 1;
}
string cmd_type = argv[1];
if (cmd_type == "session") {
bool list = false;
string kill, shell, network;
int vcpu = 0;
bool tmux = false, screen = false;
vector<string> 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<string> files;
vector<string> envs;
string env_file, env_action, env_target;
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 <action> <target>
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 == "-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, 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;
}
// Execute mode
vector<string> 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;
}