un-inception/un.cr

355 lines
11 KiB
Crystal

# 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
#!/usr/bin/env crystal
require "http/client"
require "json"
require "base64"
require "option_parser"
# Extension to language mapping
EXT_MAP = {
".jl" => "julia", ".r" => "r", ".cr" => "crystal",
".f90" => "fortran", ".cob" => "cobol", ".pro" => "prolog",
".forth" => "forth", ".4th" => "forth", ".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", ".cxx" => "cpp", ".java" => "java", ".kt" => "kotlin",
".cs" => "csharp", ".fs" => "fsharp", ".hs" => "haskell",
".ml" => "ocaml", ".clj" => "clojure", ".scm" => "scheme",
".lisp" => "commonlisp", ".erl" => "erlang", ".ex" => "elixir",
".exs" => "elixir", ".d" => "d", ".nim" => "nim", ".zig" => "zig",
".v" => "v", ".dart" => "dart", ".groovy" => "groovy",
".scala" => "scala", ".tcl" => "tcl", ".raku" => "raku", ".m" => "objc"
}
# ANSI color codes
BLUE = "\033[34m"
RED = "\033[31m"
GREEN = "\033[32m"
YELLOW = "\033[33m"
RESET = "\033[0m"
API_BASE = "https://api.unsandbox.com"
def detect_language(filename : String) : String
ext = File.extname(filename).downcase
EXT_MAP.fetch(ext, "unknown")
end
def get_api_key(args_key : String?) : String
key = args_key || ENV["UNSANDBOX_API_KEY"]?
if key.nil? || key.empty?
STDERR.puts "#{RED}Error: UNSANDBOX_API_KEY not set#{RESET}"
exit 1
end
key
end
def api_request(endpoint : String, api_key : String, method = "GET", data : JSON::Any? = nil)
url = URI.parse(API_BASE + endpoint)
headers = HTTP::Headers{
"Content-Type" => "application/json",
"Authorization" => "Bearer #{api_key}"
}
begin
response = case method
when "GET"
HTTP::Client.get(url, headers: headers)
when "POST"
body = data ? data.to_json : ""
HTTP::Client.post(url, headers: headers, body: body)
when "DELETE"
HTTP::Client.delete(url, headers: headers)
else
STDERR.puts "#{RED}Error: Unsupported method: #{method}#{RESET}"
exit 1
end
JSON.parse(response.body)
rescue ex
STDERR.puts "#{RED}Error: Request failed: #{ex.message}#{RESET}"
exit 1
end
end
def cmd_execute(args)
api_key = get_api_key(args[:api_key]?)
filename = args[:source_file].as(String)
unless File.exists?(filename)
STDERR.puts "#{RED}Error: File not found: #{filename}#{RESET}"
exit 1
end
language = detect_language(filename)
if language == "unknown"
STDERR.puts "#{RED}Error: Cannot detect language for #{filename}#{RESET}"
exit 1
end
code = File.read(filename)
# Build request payload
payload = JSON.parse({language: language, code: code}.to_json)
# Add environment variables
if env_vars = args[:env]?.as?(Array(String))
env_hash = {} of String => String
env_vars.each do |e|
if e.includes?('=')
k, v = e.split('=', 2)
env_hash[k] = v
end
end
unless env_hash.empty?
payload.as_h["env"] = JSON.parse(env_hash.to_json)
end
end
# Add input files
if files = args[:files]?.as?(Array(String))
input_files = [] of JSON::Any
files.each do |filepath|
unless File.exists?(filepath)
STDERR.puts "#{RED}Error: Input file not found: #{filepath}#{RESET}"
exit 1
end
content = Base64.strict_encode(File.read(filepath))
input_files << JSON.parse({
filename: File.basename(filepath),
content_base64: content
}.to_json)
end
unless input_files.empty?
payload.as_h["input_files"] = JSON.parse(input_files.to_json)
end
end
# Add options
if args[:artifacts]?.as?(Bool)
payload.as_h["return_artifacts"] = JSON::Any.new(true)
end
if network = args[:network]?.as?(String)
payload.as_h["network"] = JSON::Any.new(network)
end
# Execute
result = api_request("/execute", api_key, method: "POST", data: payload)
# Print output
if stdout = result["stdout"]?.try(&.as_s?)
print BLUE, stdout, RESET
end
if stderr = result["stderr"]?.try(&.as_s?)
print RED, stderr, RESET
end
# Save artifacts
if args[:artifacts]?.as?(Bool) && (artifacts = result["artifacts"]?.try(&.as_a?))
out_dir = args[:output_dir]?.as?(String) || "."
Dir.mkdir_p(out_dir)
artifacts.each do |artifact|
filename = artifact["filename"]?.try(&.as_s?) || "artifact"
content = Base64.decode(artifact["content_base64"].as_s)
path = File.join(out_dir, filename)
File.write(path, content)
File.chmod(path, 0o755)
STDERR.puts "#{GREEN}Saved: #{path}#{RESET}"
end
end
exit_code = result["exit_code"]?.try(&.as_i?) || 0
exit exit_code
end
def cmd_session(args)
api_key = get_api_key(args[:api_key]?)
if args[:list]?.as?(Bool)
result = api_request("/sessions", api_key)
sessions = result["sessions"]?.try(&.as_a?) || [] of JSON::Any
if sessions.empty?
puts "No active sessions"
else
printf "%-40s %-10s %-10s %s\n", "ID", "Shell", "Status", "Created"
sessions.each do |s|
printf "%-40s %-10s %-10s %s\n",
s["id"]?.try(&.as_s?) || "N/A",
s["shell"]?.try(&.as_s?) || "N/A",
s["status"]?.try(&.as_s?) || "N/A",
s["created_at"]?.try(&.as_s?) || "N/A"
end
end
return
end
if kill_id = args[:kill]?.as?(String)
api_request("/sessions/#{kill_id}", api_key, method: "DELETE")
puts "#{GREEN}Session terminated: #{kill_id}#{RESET}"
return
end
STDERR.puts "#{RED}Error: Use --list or --kill#{RESET}"
exit 1
end
def cmd_service(args)
api_key = get_api_key(args[:api_key]?)
if args[:list]?.as?(Bool)
result = api_request("/services", api_key)
services = result["services"]?.try(&.as_a?) || [] of JSON::Any
if services.empty?
puts "No services"
else
printf "%-20s %-15s %-10s %-15s %s\n", "ID", "Name", "Status", "Ports", "Domains"
services.each do |s|
ports = s["ports"]?.try(&.as_a?.map(&.as_i).join(',')) || ""
domains = s["domains"]?.try(&.as_a?.map(&.as_s).join(',')) || ""
printf "%-20s %-15s %-10s %-15s %s\n",
s["id"]?.try(&.as_s?) || "N/A",
s["name"]?.try(&.as_s?) || "N/A",
s["status"]?.try(&.as_s?) || "N/A",
ports, domains
end
end
return
end
if info_id = args[:info]?.as?(String)
result = api_request("/services/#{info_id}", api_key)
puts result.to_pretty_json
return
end
if logs_id = args[:logs]?.as?(String)
result = api_request("/services/#{logs_id}/logs", api_key)
puts result["logs"]?.try(&.as_s?) || ""
return
end
if sleep_id = args[:sleep]?.as?(String)
api_request("/services/#{sleep_id}/sleep", api_key, method: "POST")
puts "#{GREEN}Service sleeping: #{sleep_id}#{RESET}"
return
end
if wake_id = args[:wake]?.as?(String)
api_request("/services/#{wake_id}/wake", api_key, method: "POST")
puts "#{GREEN}Service waking: #{wake_id}#{RESET}"
return
end
if destroy_id = args[:destroy]?.as?(String)
api_request("/services/#{destroy_id}", api_key, method: "DELETE")
puts "#{GREEN}Service destroyed: #{destroy_id}#{RESET}"
return
end
STDERR.puts "#{RED}Error: Use --list, --info, --logs, --sleep, --wake, or --destroy#{RESET}"
exit 1
end
def main
args = {
source_file: nil,
api_key: nil,
network: nil,
env: [] of String,
files: [] of String,
artifacts: false,
output_dir: nil,
command: nil,
list: false,
kill: nil,
info: nil,
logs: nil,
sleep: nil,
wake: nil,
destroy: nil
} of Symbol => (String | Array(String) | Bool | Nil)
parser = OptionParser.new do |opts|
opts.banner = "Usage: un.cr [options] <source_file>\n un.cr session [options]\n un.cr service [options]"
opts.on("-k API_KEY", "--api-key=API_KEY", "API key") { |k| args[:api_key] = k }
opts.on("-n NETWORK", "--network=NETWORK", "Network mode") { |n| args[:network] = n }
opts.on("-e ENV", "--env=ENV", "Environment variable (KEY=VALUE)") { |e| args[:env].as(Array(String)) << e }
opts.on("-f FILE", "--files=FILE", "Input file") { |f| args[:files].as(Array(String)) << f }
opts.on("-a", "--artifacts", "Return artifacts") { args[:artifacts] = true }
opts.on("-o DIR", "--output-dir=DIR", "Output directory") { |d| args[:output_dir] = d }
opts.on("-l", "--list", "List items") { args[:list] = true }
opts.on("--kill=ID", "Kill session") { |id| args[:kill] = id }
opts.on("--info=ID", "Get service info") { |id| args[:info] = id }
opts.on("--logs=ID", "Get service logs") { |id| args[:logs] = id }
opts.on("--sleep=ID", "Sleep service") { |id| args[:sleep] = id }
opts.on("--wake=ID", "Wake service") { |id| args[:wake] = id }
opts.on("--destroy=ID", "Destroy service") { |id| args[:destroy] = id }
opts.unknown_args do |before, after|
if before.size > 0
case before[0]
when "session"
args[:command] = "session"
when "service"
args[:command] = "service"
else
args[:source_file] = before[0]
end
end
end
end
parser.parse
if args[:command] == "session"
cmd_session(args)
elsif args[:command] == "service"
cmd_service(args)
elsif args[:source_file]
cmd_execute(args)
else
STDERR.puts parser
exit 1
end
end
main