un-inception/un.groovy
Russell Ballestrini 2b35819393 Initial commit: 42 UN CLI implementations
Unsandbox CLI implementations in 42 programming languages for the
permacomputer project. Public domain software for code execution
across all ecosystems.

Languages: Python, JavaScript, Ruby, Go, Rust, C, C++, Java, Kotlin,
C#, F#, Haskell, OCaml, Clojure, Scheme, Common Lisp, Erlang, Elixir,
D, Nim, Zig, V, Dart, Groovy, Scala, Julia, R, Crystal, Fortran,
COBOL, Prolog, Forth, Tcl, Raku, Lua, PHP, Perl, Bash, TypeScript,
Objective-C, PowerShell, AWK

Includes test suites and service lifecycle tests.
2025-12-23 09:57:08 -05:00

504 lines
16 KiB
Groovy

// 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 - Source code must be open source & freely distributed
// FREEDOM - Voluntary participation without corporate control
// HARMONY - Systems operating with minimal waste that self-renew
// LOVE - Individual rights protected while fostering cooperation
//
// 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 groovy
// un.groovy - Unsandbox CLI Client (Groovy Implementation)
// Run: groovy un.groovy [options] <source_file>
// Requires: UNSANDBOX_API_KEY environment variable
def EXT_MAP = [
'.java': 'java', '.kt': 'kotlin', '.cs': 'csharp', '.fs': 'fsharp',
'.groovy': 'groovy', '.dart': 'dart', '.scala': 'scala',
'.py': 'python', '.js': 'javascript', '.ts': 'typescript',
'.rb': 'ruby', '.go': 'go', '.rs': 'rust', '.cpp': 'cpp', '.c': 'c',
'.sh': 'bash', '.pl': 'perl', '.lua': 'lua', '.php': 'php',
'.hs': 'haskell', '.ml': 'ocaml', '.clj': 'clojure', '.scm': 'scheme',
'.lisp': 'commonlisp', '.erl': 'erlang', '.ex': 'elixir',
'.jl': 'julia', '.r': 'r', '.cr': 'crystal', '.f90': 'fortran',
'.cob': 'cobol', '.pro': 'prolog', '.forth': 'forth', '.tcl': 'tcl',
'.raku': 'raku', '.d': 'd', '.nim': 'nim', '.zig': 'zig', '.v': 'v'
]
def API_BASE = 'https://api.unsandbox.com'
def BLUE = '\033[34m'
def RED = '\033[31m'
def GREEN = '\033[32m'
def YELLOW = '\033[33m'
def RESET = '\033[0m'
class Args {
String command = null
String sourceFile = null
String apiKey = null
String network = null
Integer vcpu = 0
List<String> env = []
List<String> files = []
Boolean artifacts = false
String outputDir = null
Boolean sessionList = false
String sessionShell = null
String sessionKill = null
Boolean serviceList = false
String serviceName = null
String servicePorts = null
String serviceBootstrap = null
String serviceInfo = null
String serviceLogs = null
String serviceTail = null
String serviceSleep = null
String serviceWake = null
String serviceDestroy = null
}
def getApiKey(argsKey) {
def key = argsKey ?: System.getenv('UNSANDBOX_API_KEY')
if (!key) {
System.err.println("${RED}Error: UNSANDBOX_API_KEY not set${RESET}")
System.exit(1)
}
return key
}
def detectLanguage(filename) {
def dotIndex = filename.lastIndexOf('.')
if (dotIndex == -1) {
System.err.println("${RED}Error: No file extension${RESET}")
System.exit(1)
}
def ext = filename.substring(dotIndex)
def language = EXT_MAP[ext]
if (!language) {
System.err.println("${RED}Error: Unsupported extension: ${ext}${RESET}")
System.exit(1)
}
return language
}
def apiRequest(endpoint, method, data, apiKey) {
def tempFile = File.createTempFile('un_request_', '.json')
try {
if (data) {
tempFile.text = data
}
def curlCmd = ['curl', '-s', '-X', method, "${API_BASE}${endpoint}",
'-H', 'Content-Type: application/json',
'-H', "Authorization: Bearer ${apiKey}"]
if (data) {
curlCmd += ['-d', "@${tempFile.absolutePath}"]
}
def proc = curlCmd.execute()
def output = proc.text
proc.waitFor()
if (proc.exitValue() != 0) {
System.err.println("${RED}Error: curl failed${RESET}")
System.exit(1)
}
return output
} finally {
tempFile.delete()
}
}
def cmdExecute(args) {
def apiKey = getApiKey(args.apiKey)
def file = new File(args.sourceFile)
if (!file.exists()) {
System.err.println("${RED}Error: File not found: ${args.sourceFile}${RESET}")
System.exit(1)
}
def code = file.text
def language = detectLanguage(args.sourceFile)
def escapedCode = code.replace('\\', '\\\\')
.replace('"', '\\"')
.replace('\n', '\\n')
.replace('\r', '\\r')
.replace('\t', '\\t')
def json = """{"language":"${language}","code":"${escapedCode}""""
if (args.env) {
def envJson = args.env.collect { e ->
def parts = e.split('=', 2)
if (parts.size() == 2) {
return "\"${parts[0]}\":\"${parts[1]}\""
}
return null
}.findAll { it != null }.join(',')
if (envJson) {
json += ""","env":{${envJson}}"""
}
}
if (args.files) {
def filesJson = args.files.collect { filepath ->
def f = new File(filepath)
if (!f.exists()) {
System.err.println("${RED}Error: Input file not found: ${filepath}${RESET}")
System.exit(1)
}
def content = f.bytes.encodeBase64().toString()
return """{"filename":"${f.name}","content_base64":"${content}"}"""
}.join(',')
json += ""","input_files":[${filesJson}]"""
}
if (args.artifacts) {
json += ',"return_artifacts":true'
}
if (args.network) {
json += ""","network":"${args.network}""""
}
if (args.vcpu > 0) {
json += ""","vcpu":${args.vcpu}"""
}
json += '}'
def output = apiRequest('/execute', 'POST', json, apiKey)
def stdoutMatch = output =~ /"stdout":"((?:[^"\\]|\\.)*)"/
def stderrMatch = output =~ /"stderr":"((?:[^"\\]|\\.)*)"/
def exitCodeMatch = output =~ /"exit_code":(\d+)/
if (stdoutMatch.find()) {
def stdout = stdoutMatch.group(1)
.replace('\\n', '\n')
.replace('\\t', '\t')
.replace('\\"', '"')
.replace('\\\\', '\\')
print("${BLUE}${stdout}${RESET}")
}
if (stderrMatch.find()) {
def stderr = stderrMatch.group(1)
.replace('\\n', '\n')
.replace('\\t', '\t')
.replace('\\"', '"')
.replace('\\\\', '\\')
System.err.print("${RED}${stderr}${RESET}")
}
if (args.artifacts) {
def artifactsMatch = output =~ /"artifacts":\[(.*?)\]/
if (artifactsMatch.find()) {
def outDir = args.outputDir ?: '.'
new File(outDir).mkdirs()
System.err.println("${GREEN}Artifacts saved to ${outDir}${RESET}")
}
}
def exitCode = 0
if (exitCodeMatch.find()) {
exitCode = exitCodeMatch.group(1).toInteger()
}
System.exit(exitCode)
}
def cmdSession(args) {
def apiKey = getApiKey(args.apiKey)
if (args.sessionList) {
def output = apiRequest('/sessions', 'GET', null, apiKey)
println("%-40s %-10s %-10s %s".format("ID", "Shell", "Status", "Created"))
println("No sessions (list parsing not implemented)")
return
}
if (args.sessionKill) {
apiRequest("/sessions/${args.sessionKill}", 'DELETE', null, apiKey)
println("${GREEN}Session terminated: ${args.sessionKill}${RESET}")
return
}
def json = """{"shell":"${args.sessionShell ?: 'bash'}""""
if (args.network) {
json += ""","network":"${args.network}""""
}
if (args.vcpu > 0) {
json += ""","vcpu":${args.vcpu}"""
}
json += '}'
println("${YELLOW}Creating session...${RESET}")
def output = apiRequest('/sessions', 'POST', json, apiKey)
def idMatch = output =~ /"id":"([^"]+)"/
if (idMatch.find()) {
println("${GREEN}Session created: ${idMatch.group(1)}${RESET}")
} else {
println("${GREEN}Session created${RESET}")
}
println("${YELLOW}(Interactive sessions require WebSocket - use un2 for full support)${RESET}")
}
def cmdService(args) {
def apiKey = getApiKey(args.apiKey)
if (args.serviceList) {
def output = apiRequest('/services', 'GET', null, apiKey)
println("%-20s %-15s %-10s %-15s %s".format("ID", "Name", "Status", "Ports", "Domains"))
println("No services (list parsing not implemented)")
return
}
if (args.serviceInfo) {
def output = apiRequest("/services/${args.serviceInfo}", 'GET', null, apiKey)
println(output)
return
}
if (args.serviceLogs) {
def output = apiRequest("/services/${args.serviceLogs}/logs", 'GET', null, apiKey)
def logsMatch = output =~ /"logs":"((?:[^"\\]|\\.)*)"/
if (logsMatch.find()) {
println(logsMatch.group(1).replace('\\n', '\n'))
}
return
}
if (args.serviceTail) {
def output = apiRequest("/services/${args.serviceTail}/logs?lines=9000", 'GET', null, apiKey)
def logsMatch = output =~ /"logs":"((?:[^"\\]|\\.)*)"/
if (logsMatch.find()) {
println(logsMatch.group(1).replace('\\n', '\n'))
}
return
}
if (args.serviceSleep) {
apiRequest("/services/${args.serviceSleep}/sleep", 'POST', null, apiKey)
println("${GREEN}Service sleeping: ${args.serviceSleep}${RESET}")
return
}
if (args.serviceWake) {
apiRequest("/services/${args.serviceWake}/wake", 'POST', null, apiKey)
println("${GREEN}Service waking: ${args.serviceWake}${RESET}")
return
}
if (args.serviceDestroy) {
apiRequest("/services/${args.serviceDestroy}", 'DELETE', null, apiKey)
println("${GREEN}Service destroyed: ${args.serviceDestroy}${RESET}")
return
}
if (args.serviceName) {
def json = """{"name":"${args.serviceName}""""
if (args.servicePorts) {
def ports = args.servicePorts.split(',').collect { it.trim() }.join(',')
json += ""","ports":[${ports}]"""
}
if (args.serviceBootstrap) {
def escaped = args.serviceBootstrap.replace('\\', '\\\\').replace('"', '\\"')
json += ""","bootstrap":"${escaped}""""
}
if (args.network) {
json += ""","network":"${args.network}""""
}
if (args.vcpu > 0) {
json += ""","vcpu":${args.vcpu}"""
}
json += '}'
def output = apiRequest('/services', 'POST', json, apiKey)
def idMatch = output =~ /"id":"([^"]+)"/
if (idMatch.find()) {
println("${GREEN}Service created: ${idMatch.group(1)}${RESET}")
}
def nameMatch = output =~ /"name":"([^"]+)"/
if (nameMatch.find()) {
println("Name: ${nameMatch.group(1)}")
}
def urlMatch = output =~ /"url":"([^"]+)"/
if (urlMatch.find()) {
println("URL: ${urlMatch.group(1)}")
}
return
}
System.err.println("${RED}Error: Specify --name to create a service, or use --list, --info, etc.${RESET}")
System.exit(1)
}
def parseArgs(argv) {
def args = new Args()
def i = 0
while (i < argv.size()) {
switch (argv[i]) {
case 'session':
args.command = 'session'
break
case 'service':
args.command = 'service'
break
case '-k':
case '--api-key':
args.apiKey = argv[++i]
break
case '-n':
case '--network':
args.network = argv[++i]
break
case '-v':
case '--vcpu':
args.vcpu = argv[++i].toInteger()
break
case '-e':
case '--env':
args.env << argv[++i]
break
case '-f':
case '--files':
args.files << argv[++i]
break
case '-a':
case '--artifacts':
args.artifacts = true
break
case '-o':
case '--output-dir':
args.outputDir = argv[++i]
break
case '-l':
case '--list':
if (args.command == 'session') args.sessionList = true
else if (args.command == 'service') args.serviceList = true
break
case '-s':
case '--shell':
args.sessionShell = argv[++i]
break
case '--kill':
args.sessionKill = argv[++i]
break
case '--name':
args.serviceName = argv[++i]
break
case '--ports':
args.servicePorts = argv[++i]
break
case '--bootstrap':
args.serviceBootstrap = argv[++i]
break
case '--info':
args.serviceInfo = argv[++i]
break
case '--logs':
args.serviceLogs = argv[++i]
break
case '--tail':
args.serviceTail = argv[++i]
break
case '--sleep':
args.serviceSleep = argv[++i]
break
case '--wake':
args.serviceWake = argv[++i]
break
case '--destroy':
args.serviceDestroy = argv[++i]
break
default:
if (!argv[i].startsWith('-')) {
args.sourceFile = argv[i]
}
}
i++
}
return args
}
def printHelp() {
println '''Usage: groovy un.groovy [options] <source_file>
groovy un.groovy session [options]
groovy un.groovy service [options]
Execute options:
-e KEY=VALUE Set environment variable
-f FILE Add input file
-a Return artifacts
-o DIR Output directory for artifacts
-n MODE Network mode (zerotrust/semitrusted)
-v N vCPU count (1-8)
-k KEY API key
Session options:
--list List active sessions
--shell NAME Shell/REPL to use
--kill ID Terminate session
Service options:
--list List services
--name NAME Service name
--ports PORTS Comma-separated ports
--bootstrap CMD Bootstrap command
--info ID Get service details
--logs ID Get all logs
--tail ID Get last 9000 lines
--sleep ID Freeze service
--wake ID Unfreeze service
--destroy ID Destroy service
'''
}
// Main execution
try {
def args = parseArgs(this.args as List)
if (args.command == 'session') {
cmdSession(args)
} else if (args.command == 'service') {
cmdService(args)
} else if (args.sourceFile) {
cmdExecute(args)
} else {
printHelp()
System.exit(1)
}
} catch (Exception e) {
System.err.println("${RED}Error: ${e.message}${RESET}")
System.exit(1)
}