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
542
un.go
Normal file
542
un.go
Normal file
|
|
@ -0,0 +1,542 @@
|
|||
// 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
|
||||
|
||||
|
||||
// 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, 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 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)")
|
||||
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, *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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue