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.
This commit is contained in:
commit
2b35819393
143 changed files with 30022 additions and 0 deletions
504
un.groovy
Normal file
504
un.groovy
Normal file
|
|
@ -0,0 +1,504 @@
|
|||
// 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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue