un-inception/un.go
Russell Ballestrini eb5d4ca8bf Add --type option for services across all 42 implementations
Support service_type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp).
Each implementation now parses --type and sends service_type in the JSON payload.
2025-12-26 08:39:34 -05:00

546 lines
16 KiB
Go

// 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 - Go Implementation
// Compile: go build -o un_go un.go
// Usage:
// un.go script.py
// un.go -e KEY=VALUE -f data.txt script.py
// un.go session --list
// un.go service --name web --ports 8080 --bootstrap "python -m http.server"
package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
)
const (
APIBase = "https://api.unsandbox.com"
Blue = "\033[34m"
Red = "\033[31m"
Green = "\033[32m"
Yellow = "\033[33m"
Reset = "\033[0m"
)
var extMap = map[string]string{
".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",
".jl": "julia", ".r": "r", ".cr": "crystal",
".d": "d", ".nim": "nim", ".zig": "zig", ".v": "v",
".dart": "dart", ".groovy": "groovy", ".scala": "scala",
".f90": "fortran", ".f95": "fortran", ".cob": "cobol",
".pro": "prolog", ".forth": "forth", ".4th": "forth",
".tcl": "tcl", ".raku": "raku", ".m": "objc",
}
type envVars []string
func (e *envVars) String() string { return "" }
func (e *envVars) Set(value string) error {
*e = append(*e, value)
return nil
}
type inputFiles []string
func (i *inputFiles) String() string { return "" }
func (i *inputFiles) Set(value string) error {
*i = append(*i, value)
return nil
}
func detectLanguage(filename string) (string, error) {
ext := strings.ToLower(filepath.Ext(filename))
if lang, ok := extMap[ext]; ok {
return lang, nil
}
// Try shebang
data, err := os.ReadFile(filename)
if err == nil {
firstLine := strings.Split(string(data), "\n")[0]
if strings.HasPrefix(firstLine, "#!") {
if strings.Contains(firstLine, "python") {
return "python", nil
}
if strings.Contains(firstLine, "node") {
return "javascript", nil
}
if strings.Contains(firstLine, "ruby") {
return "ruby", nil
}
if strings.Contains(firstLine, "bash") || strings.Contains(firstLine, "/sh") {
return "bash", nil
}
}
}
return "", fmt.Errorf("cannot detect language from extension")
}
func getAPIKey(keyArg string) string {
if keyArg != "" {
return keyArg
}
key := os.Getenv("UNSANDBOX_API_KEY")
if key == "" {
fmt.Fprintf(os.Stderr, "%sError: UNSANDBOX_API_KEY not set%s\n", Red, Reset)
os.Exit(1)
}
return key
}
func apiRequest(endpoint, method string, data map[string]interface{}, apiKey string) map[string]interface{} {
url := APIBase + endpoint
var reqBody io.Reader
if data != nil {
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError marshaling JSON: %v%s\n", Red, err, Reset)
os.Exit(1)
}
reqBody = bytes.NewBuffer(jsonData)
}
req, err := http.NewRequest(method, url, reqBody)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError creating request: %v%s\n", Red, err, Reset)
os.Exit(1)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError making request: %v%s\n", Red, err, Reset)
os.Exit(1)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError reading response: %v%s\n", Red, err, Reset)
os.Exit(1)
}
if resp.StatusCode >= 400 {
fmt.Fprintf(os.Stderr, "%sError: HTTP %d - %s%s\n", Red, resp.StatusCode, string(body), Reset)
os.Exit(1)
}
var result map[string]interface{}
if err := json.Unmarshal(body, &result); err != nil {
fmt.Fprintf(os.Stderr, "%sError parsing response: %v%s\n", Red, err, Reset)
os.Exit(1)
}
return result
}
func cmdExecute(sourceFile string, envs envVars, files inputFiles, artifacts bool, outputDir, network string, vcpu int, apiKey string) {
code, err := os.ReadFile(sourceFile)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError reading file: %v%s\n", Red, err, Reset)
os.Exit(1)
}
language, err := detectLanguage(sourceFile)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError: %v%s\n", Red, err, Reset)
os.Exit(1)
}
payload := map[string]interface{}{
"language": language,
"code": string(code),
}
// Environment variables
if len(envs) > 0 {
envMap := make(map[string]string)
for _, e := range envs {
parts := strings.SplitN(e, "=", 2)
if len(parts) == 2 {
envMap[parts[0]] = parts[1]
}
}
if len(envMap) > 0 {
payload["env"] = envMap
}
}
// Input files
if len(files) > 0 {
var inputFilesList []map[string]string
for _, f := range files {
content, err := os.ReadFile(f)
if err != nil {
fmt.Fprintf(os.Stderr, "%sError reading input file %s: %v%s\n", Red, f, err, Reset)
os.Exit(1)
}
inputFilesList = append(inputFilesList, map[string]string{
"filename": filepath.Base(f),
"content_base64": base64.StdEncoding.EncodeToString(content),
})
}
payload["input_files"] = inputFilesList
}
if artifacts {
payload["return_artifacts"] = true
}
if network != "" {
payload["network"] = network
}
if vcpu > 0 {
payload["vcpu"] = vcpu
}
result := apiRequest("/execute", "POST", payload, apiKey)
// Print output
if stdout, ok := result["stdout"].(string); ok && stdout != "" {
fmt.Printf("%s%s%s", Blue, stdout, Reset)
}
if stderr, ok := result["stderr"].(string); ok && stderr != "" {
fmt.Fprintf(os.Stderr, "%s%s%s", Red, stderr, Reset)
}
// Save artifacts
if artifacts {
if artList, ok := result["artifacts"].([]interface{}); ok {
outDir := outputDir
if outDir == "" {
outDir = "."
}
os.MkdirAll(outDir, 0755)
for _, art := range artList {
artMap := art.(map[string]interface{})
filename := "artifact"
if fn, ok := artMap["filename"].(string); ok {
filename = fn
}
content, _ := base64.StdEncoding.DecodeString(artMap["content_base64"].(string))
path := filepath.Join(outDir, filename)
os.WriteFile(path, content, 0755)
fmt.Fprintf(os.Stderr, "%sSaved: %s%s\n", Green, path, Reset)
}
}
}
exitCode := 0
if ec, ok := result["exit_code"].(float64); ok {
exitCode = int(ec)
}
os.Exit(exitCode)
}
func cmdSession(sessionList, sessionKill, sessionShell, network string, vcpu int, tmux, screen bool, apiKey string) {
if sessionList != "" {
result := apiRequest("/sessions", "GET", nil, apiKey)
sessions := result["sessions"].([]interface{})
if len(sessions) == 0 {
fmt.Println("No active sessions")
} else {
fmt.Printf("%-40s %-10s %-10s %s\n", "ID", "Shell", "Status", "Created")
for _, s := range sessions {
sess := s.(map[string]interface{})
fmt.Printf("%-40s %-10s %-10s %s\n",
sess["id"], sess["shell"], sess["status"], sess["created_at"])
}
}
return
}
if sessionKill != "" {
apiRequest("/sessions/"+sessionKill, "DELETE", nil, apiKey)
fmt.Printf("%sSession terminated: %s%s\n", Green, sessionKill, Reset)
return
}
// Create session
payload := map[string]interface{}{}
if sessionShell != "" {
payload["shell"] = sessionShell
} else {
payload["shell"] = "bash"
}
if network != "" {
payload["network"] = network
}
if vcpu > 0 {
payload["vcpu"] = vcpu
}
if tmux {
payload["persistence"] = "tmux"
}
if screen {
payload["persistence"] = "screen"
}
fmt.Printf("%sCreating session...%s\n", Yellow, Reset)
result := apiRequest("/sessions", "POST", payload, apiKey)
fmt.Printf("%sSession created: %s%s\n", Green, result["id"], Reset)
}
func cmdService(serviceName, servicePorts, serviceDomains, serviceType, serviceBootstrap, serviceList, serviceInfo, serviceLogs, serviceTail, serviceSleep, serviceWake, serviceDestroy, network string, vcpu int, apiKey string) {
if serviceList != "" {
result := apiRequest("/services", "GET", nil, apiKey)
services := result["services"].([]interface{})
if len(services) == 0 {
fmt.Println("No services")
} else {
fmt.Printf("%-20s %-15s %-10s %-15s %s\n", "ID", "Name", "Status", "Ports", "Domains")
for _, s := range services {
svc := s.(map[string]interface{})
ports := ""
if p, ok := svc["ports"].([]interface{}); ok {
var ps []string
for _, port := range p {
ps = append(ps, fmt.Sprintf("%v", port))
}
ports = strings.Join(ps, ",")
}
domains := ""
if d, ok := svc["domains"].([]interface{}); ok {
var ds []string
for _, domain := range d {
ds = append(ds, domain.(string))
}
domains = strings.Join(ds, ",")
}
fmt.Printf("%-20s %-15s %-10s %-15s %s\n",
svc["id"], svc["name"], svc["status"], ports, domains)
}
}
return
}
if serviceInfo != "" {
result := apiRequest("/services/"+serviceInfo, "GET", nil, apiKey)
jsonData, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(jsonData))
return
}
if serviceLogs != "" {
result := apiRequest("/services/"+serviceLogs+"/logs", "GET", nil, apiKey)
fmt.Print(result["logs"])
return
}
if serviceTail != "" {
result := apiRequest("/services/"+serviceTail+"/logs?lines=9000", "GET", nil, apiKey)
fmt.Print(result["logs"])
return
}
if serviceSleep != "" {
apiRequest("/services/"+serviceSleep+"/sleep", "POST", nil, apiKey)
fmt.Printf("%sService sleeping: %s%s\n", Green, serviceSleep, Reset)
return
}
if serviceWake != "" {
apiRequest("/services/"+serviceWake+"/wake", "POST", nil, apiKey)
fmt.Printf("%sService waking: %s%s\n", Green, serviceWake, Reset)
return
}
if serviceDestroy != "" {
apiRequest("/services/"+serviceDestroy, "DELETE", nil, apiKey)
fmt.Printf("%sService destroyed: %s%s\n", Green, serviceDestroy, Reset)
return
}
// Create service
if serviceName != "" {
payload := map[string]interface{}{"name": serviceName}
if servicePorts != "" {
var ports []int
for _, p := range strings.Split(servicePorts, ",") {
port, _ := strconv.Atoi(strings.TrimSpace(p))
ports = append(ports, port)
}
payload["ports"] = ports
}
if serviceDomains != "" {
payload["domains"] = strings.Split(serviceDomains, ",")
}
if serviceType != "" {
payload["service_type"] = serviceType
}
if serviceBootstrap != "" {
// Check if it's a file
if _, err := os.Stat(serviceBootstrap); err == nil {
content, _ := os.ReadFile(serviceBootstrap)
payload["bootstrap"] = string(content)
} else {
payload["bootstrap"] = serviceBootstrap
}
}
if network != "" {
payload["network"] = network
}
if vcpu > 0 {
payload["vcpu"] = vcpu
}
result := apiRequest("/services", "POST", payload, apiKey)
fmt.Printf("%sService created: %s%s\n", Green, result["id"], Reset)
fmt.Printf("Name: %s\n", result["name"])
if url, ok := result["url"]; ok {
fmt.Printf("URL: %s\n", url)
}
return
}
fmt.Fprintf(os.Stderr, "%sError: Specify --name to create a service, or use --list, --info, etc.%s\n", Red, Reset)
os.Exit(1)
}
func main() {
// Common flags
apiKey := flag.String("k", "", "API key (or set UNSANDBOX_API_KEY)")
network := flag.String("n", "", "Network mode (zerotrust|semitrusted)")
vcpu := flag.Int("v", 0, "vCPU count (1-8)")
// Execute flags
var envs envVars
var files inputFiles
flag.Var(&envs, "e", "Environment variable (KEY=VALUE)")
flag.Var(&files, "f", "Input file")
artifacts := flag.Bool("a", false, "Return artifacts")
outputDir := flag.String("o", "", "Output directory for artifacts")
// Session flags
sessionCmd := flag.NewFlagSet("session", flag.ExitOnError)
sessionList := sessionCmd.String("list", "", "List active sessions")
sessionKill := sessionCmd.String("kill", "", "Kill session by ID")
sessionShell := sessionCmd.String("shell", "", "Shell/REPL to use")
sessionTmux := sessionCmd.Bool("tmux", false, "Enable tmux persistence")
sessionScreen := sessionCmd.Bool("screen", false, "Enable screen persistence")
sessionNetwork := sessionCmd.String("n", "", "Network mode")
sessionVcpu := sessionCmd.Int("v", 0, "vCPU count")
sessionKey := sessionCmd.String("k", "", "API key")
// Service flags
serviceCmd := flag.NewFlagSet("service", flag.ExitOnError)
serviceName := serviceCmd.String("name", "", "Service name")
servicePorts := serviceCmd.String("ports", "", "Ports (comma-separated)")
serviceDomains := serviceCmd.String("domains", "", "Custom domains (comma-separated)")
serviceType := serviceCmd.String("type", "", "Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)")
serviceBootstrap := serviceCmd.String("bootstrap", "", "Bootstrap command/file")
serviceList := serviceCmd.String("list", "", "List services")
serviceInfo := serviceCmd.String("info", "", "Get service info")
serviceLogs := serviceCmd.String("logs", "", "Get service logs")
serviceTail := serviceCmd.String("tail", "", "Get last 9000 lines")
serviceSleep := serviceCmd.String("sleep", "", "Freeze service")
serviceWake := serviceCmd.String("wake", "", "Unfreeze service")
serviceDestroy := serviceCmd.String("destroy", "", "Destroy service")
serviceNetwork := serviceCmd.String("n", "", "Network mode")
serviceVcpu := serviceCmd.Int("v", 0, "vCPU count")
serviceKey := serviceCmd.String("k", "", "API key")
// Parse
flag.Parse()
if len(os.Args) > 1 {
switch os.Args[1] {
case "session":
sessionCmd.Parse(os.Args[2:])
key := getAPIKey(*sessionKey)
net := *sessionNetwork
if net == "" {
net = *network
}
vc := *sessionVcpu
if vc == 0 {
vc = *vcpu
}
cmdSession(*sessionList, *sessionKill, *sessionShell, net, vc, *sessionTmux, *sessionScreen, key)
return
case "service":
serviceCmd.Parse(os.Args[2:])
key := getAPIKey(*serviceKey)
net := *serviceNetwork
if net == "" {
net = *network
}
vc := *serviceVcpu
if vc == 0 {
vc = *vcpu
}
cmdService(*serviceName, *servicePorts, *serviceDomains, *serviceType, *serviceBootstrap, *serviceList, *serviceInfo, *serviceLogs, *serviceTail, *serviceSleep, *serviceWake, *serviceDestroy, net, vc, key)
return
}
}
// Execute mode
if flag.NArg() == 0 {
fmt.Fprintf(os.Stderr, "Usage: %s [options] <source_file>\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s session [options]\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s service [options]\n", os.Args[0])
os.Exit(1)
}
sourceFile := flag.Arg(0)
key := getAPIKey(*apiKey)
cmdExecute(sourceFile, envs, files, *artifacts, *outputDir, *network, *vcpu, key)
}