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.
This commit is contained in:
Russell Ballestrini 2025-12-26 08:39:34 -05:00
parent 564e9e7075
commit eb5d4ca8bf
42 changed files with 783 additions and 104 deletions

7
Un.cs
View file

@ -331,6 +331,10 @@ class Un
}
payload["ports"] = ports;
}
if (args.ServiceType != null)
{
payload["service_type"] = args.ServiceType;
}
if (args.ServiceBootstrap != null)
{
payload["bootstrap"] = args.ServiceBootstrap;
@ -675,6 +679,7 @@ class Un
public string ServiceSleep = null;
public string ServiceWake = null;
public string ServiceDestroy = null;
public string ServiceType = null;
}
static Args ParseArgs(string[] args)
@ -701,6 +706,7 @@ class Un
else if (arg == "--kill") result.SessionKill = args[++i];
else if (arg == "--name") result.ServiceName = args[++i];
else if (arg == "--ports") result.ServicePorts = args[++i];
else if (arg == "--type") result.ServiceType = args[++i];
else if (arg == "--bootstrap") result.ServiceBootstrap = args[++i];
else if (arg == "--info") result.ServiceInfo = args[++i];
else if (arg == "--logs") result.ServiceLogs = args[++i];
@ -737,6 +743,7 @@ Service options:
--list List services
--name NAME Service name
--ports PORTS Comma-separated ports
--type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)
--bootstrap CMD Bootstrap command
--info ID Get service details
--logs ID Get all logs

View file

@ -281,6 +281,9 @@ public class Un {
}
payload.put("ports", ports);
}
if (args.serviceType != null) {
payload.put("service_type", args.serviceType);
}
if (args.serviceBootstrap != null) {
payload.put("bootstrap", args.serviceBootstrap);
}
@ -551,6 +554,7 @@ public class Un {
boolean serviceList = false;
String serviceName = null;
String servicePorts = null;
String serviceType = null;
String serviceBootstrap = null;
String serviceInfo = null;
String serviceLogs = null;
@ -593,6 +597,8 @@ public class Un {
result.serviceName = args[++i];
} else if (arg.equals("--ports")) {
result.servicePorts = args[++i];
} else if (arg.equals("--type")) {
result.serviceType = args[++i];
} else if (arg.equals("--bootstrap")) {
result.serviceBootstrap = args[++i];
} else if (arg.equals("--info")) {
@ -637,6 +643,7 @@ public class Un {
System.out.println(" --list List services");
System.out.println(" --name NAME Service name");
System.out.println(" --ports PORTS Comma-separated ports");
System.out.println(" --type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)");
System.out.println(" --bootstrap CMD Bootstrap command");
System.out.println(" --info ID Get service details");
System.out.println(" --logs ID Get all logs");

101
un.awk
View file

@ -187,13 +187,76 @@ function service_destroy(id) {
print GREEN "Service destroyed: " id RESET
}
function service_create(name, ports, domains, service_type, bootstrap) {
api_key = get_api_key()
# Build JSON payload
json = "{\"name\":\"" escape_json(name) "\""
if (ports != "") {
json = json ",\"ports\":[" ports "]"
}
if (domains != "") {
# Split domains by comma and build array
split(domains, domain_arr, ",")
json = json ",\"domains\":["
for (i in domain_arr) {
if (i > 1) json = json ","
json = json "\"" escape_json(domain_arr[i]) "\""
}
json = json "]"
}
if (service_type != "") {
json = json ",\"service_type\":\"" escape_json(service_type) "\""
}
if (bootstrap != "") {
json = json ",\"bootstrap\":\"" escape_json(bootstrap) "\""
}
json = json "}"
# Write to temp file
tmp = "/tmp/un_awk_svc_" PROCINFO["pid"] ".json"
print json > tmp
close(tmp)
# Call curl
cmd = "curl -s -X POST '" API_BASE "/services' " \
"-H 'Content-Type: application/json' " \
"-H 'Authorization: Bearer " api_key "' " \
"-d '@" tmp "'"
response = ""
while ((cmd | getline line) > 0) {
response = response line
}
close(cmd)
# Clean up
system("rm -f " tmp)
# Print response
print response
}
function show_help() {
print "Usage: awk -f un.awk <source_file>"
print " awk -f un.awk session --list"
print " awk -f un.awk session --kill ID"
print " awk -f un.awk service --list"
print " awk -f un.awk service --create --name NAME [--ports PORTS] [--domains DOMAINS] [--type TYPE] [--bootstrap CMD]"
print " awk -f un.awk service --destroy ID"
print ""
print "Service options:"
print " --name NAME Service name (required for --create)"
print " --ports PORTS Comma-separated port numbers"
print " --domains DOMAINS Comma-separated domain names"
print " --type TYPE Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)"
print " --bootstrap CMD Bootstrap command or script"
print ""
print "Requires: UNSANDBOX_API_KEY environment variable"
}
@ -230,8 +293,44 @@ END {
service_list()
} else if (ARGC >= 4 && ARGV[2] == "--destroy") {
service_destroy(ARGV[3])
} else if (ARGV[2] == "--create") {
# Parse service creation arguments
name = ""
ports = ""
domains = ""
service_type = ""
bootstrap = ""
i = 3
while (i < ARGC) {
if (ARGV[i] == "--name" && i + 1 < ARGC) {
name = ARGV[i + 1]
i += 2
} else if (ARGV[i] == "--ports" && i + 1 < ARGC) {
ports = ARGV[i + 1]
i += 2
} else if (ARGV[i] == "--domains" && i + 1 < ARGC) {
domains = ARGV[i + 1]
i += 2
} else if (ARGV[i] == "--type" && i + 1 < ARGC) {
service_type = ARGV[i + 1]
i += 2
} else if (ARGV[i] == "--bootstrap" && i + 1 < ARGC) {
bootstrap = ARGV[i + 1]
i += 2
} else {
i++
}
}
if (name == "") {
print RED "Error: --name is required for service creation" RESET > "/dev/stderr"
exit 1
}
service_create(name, ports, domains, service_type, bootstrap)
} else {
print "Usage: awk -f un.awk service --list|--destroy ID"
print "Usage: awk -f un.awk service --list|--create|--destroy ID"
}
exit 0
}

54
un.clj
View file

@ -173,7 +173,7 @@
(println (str yellow "Session created (WebSocket required)" reset))
(println (curl-post api-key "/sessions" json))))))
(defn service-command [action sid name ports bootstrap network vcpu]
(defn service-command [action sid name ports bootstrap service-type network vcpu]
(let [api-key (get-api-key)]
(case action
:list (println (curl-get api-key "/services"))
@ -191,9 +191,10 @@
:create (when name
(let [ports-json (if ports (str ",\"ports\":[" ports "]") "")
bootstrap-json (if bootstrap (str ",\"bootstrap\":\"" (escape-json bootstrap) "\"") "")
service-type-json (if service-type (str ",\"service_type\":\"" service-type "\"") "")
network-json (if network (str ",\"network\":\"" network "\"") "")
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
json (str "{\"name\":\"" name "\"" ports-json bootstrap-json network-json vcpu-json "}")]
json (str "{\"name\":\"" name "\"" ports-json bootstrap-json service-type-json network-json vcpu-json "}")]
(println (str green "Service created" reset))
(println (curl-post api-key "/services" json)))))))
@ -213,12 +214,13 @@
service-name nil
service-ports nil
service-bootstrap nil
service-type nil
mode :execute]
(cond
(empty? args)
(case mode
:session (session-command (or session-action :create) session-id session-shell network vcpu)
:service (service-command (or service-action :create) service-id service-name service-ports service-bootstrap network vcpu)
:service (service-command (or service-action :create) service-id service-name service-ports service-bootstrap service-type network vcpu)
:execute (if file
(execute-command file env-vars artifacts out-dir network vcpu)
(do (println "Usage: un.clj [options] <source_file>")
@ -228,91 +230,95 @@
(= (first args) "session")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap :session)
service-action service-id service-name service-ports service-bootstrap service-type :session)
(= (first args) "service")
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap :service)
service-action service-id service-name service-ports service-bootstrap service-type :service)
;; Session options
(and (= mode :session) (= (first args) "--list"))
(recur (rest args) file env-vars artifacts out-dir network vcpu :list session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
(and (= mode :session) (= (first args) "--kill"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu :kill (second args) session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
(and (= mode :session) (or (= (first args) "--shell") (= (first args) "-s")))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id (second args)
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
;; Service options
(and (= mode :service) (= (first args) "--list"))
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:list service-id service-name service-ports service-bootstrap mode)
:list service-id service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--info"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:info (second args) service-name service-ports service-bootstrap mode)
:info (second args) service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--logs"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:logs (second args) service-name service-ports service-bootstrap mode)
:logs (second args) service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--sleep"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:sleep (second args) service-name service-ports service-bootstrap mode)
:sleep (second args) service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--wake"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:wake (second args) service-name service-ports service-bootstrap mode)
:wake (second args) service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--destroy"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:destroy (second args) service-name service-ports service-bootstrap mode)
:destroy (second args) service-name service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--name"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
:create service-id (second args) service-ports service-bootstrap mode)
:create service-id (second args) service-ports service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--ports"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name (second args) service-bootstrap mode)
service-action service-id service-name (second args) service-bootstrap service-type mode)
(and (= mode :service) (= (first args) "--bootstrap"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports (second args) mode)
service-action service-id service-name service-ports (second args) service-type mode)
(and (= mode :service) (= (first args) "--type"))
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap (second args) mode)
;; Execute options
(= (first args) "-e")
(let [[k v] (str/split (second args) #"=" 2)]
(recur (rest (rest args)) file (conj env-vars [k v]) artifacts out-dir network vcpu
session-action session-id session-shell service-action service-id service-name service-ports service-bootstrap mode))
session-action session-id session-shell service-action service-id service-name service-ports service-bootstrap service-type mode))
(= (first args) "-a")
(recur (rest args) file env-vars true out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
(= (first args) "-o")
(recur (rest (rest args)) file env-vars artifacts (second args) network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
(= (first args) "-n")
(recur (rest (rest args)) file env-vars artifacts out-dir (second args) vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
(= (first args) "-v")
(recur (rest (rest args)) file env-vars artifacts out-dir network (Integer/parseInt (second args)) session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
;; Source file
(and (= mode :execute) (not (.startsWith (first args) "-")) (nil? file))
(recur (rest args) (first args) env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode)
service-action service-id service-name service-ports service-bootstrap service-type mode)
:else
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell
service-action service-id service-name service-ports service-bootstrap mode))))
service-action service-id service-name service-ports service-bootstrap service-type mode))))
(parse-args *command-line-args*)

94
un.cob
View file

@ -68,6 +68,11 @@
01 WS-COMMAND PIC X(32).
01 WS-OPERATION PIC X(32).
01 WS-ID PIC X(256).
01 WS-NAME PIC X(256).
01 WS-PORTS PIC X(256).
01 WS-DOMAINS PIC X(256).
01 WS-SERVICE-TYPE PIC X(64).
01 WS-BOOTSTRAP PIC X(2048).
PROCEDURE DIVISION.
MAIN-PROCEDURE.
@ -166,6 +171,13 @@
STOP RUN
END-IF.
* Initialize service parameters
MOVE SPACES TO WS-NAME.
MOVE SPACES TO WS-PORTS.
MOVE SPACES TO WS-DOMAINS.
MOVE SPACES TO WS-SERVICE-TYPE.
MOVE SPACES TO WS-BOOTSTRAP.
* Parse service arguments
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
@ -186,9 +198,13 @@
ELSE IF WS-ARG2 = "--destroy"
ACCEPT WS-ID FROM ARGUMENT-VALUE
PERFORM SERVICE-DESTROY
ELSE IF WS-ARG2 = "--name"
ACCEPT WS-NAME FROM ARGUMENT-VALUE
PERFORM PARSE-SERVICE-CREATE-ARGS
PERFORM SERVICE-CREATE
ELSE
DISPLAY "Error: Use --list, --info, --logs, "
"--sleep, --wake, or --destroy" UPON SYSERR
"--sleep, --wake, --destroy, or --name" UPON SYSERR
MOVE 1 TO RETURN-CODE
END-IF.
@ -350,3 +366,79 @@
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.
PARSE-SERVICE-CREATE-ARGS.
* Parse remaining arguments for service creation
* This is a simplified parser that looks for specific flags
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE.
PERFORM UNTIL WS-ARG3 = SPACES
IF WS-ARG3 = "--ports"
ACCEPT WS-PORTS FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--domains"
ACCEPT WS-DOMAINS FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--type"
ACCEPT WS-SERVICE-TYPE FROM ARGUMENT-VALUE
ELSE IF WS-ARG3 = "--bootstrap"
ACCEPT WS-BOOTSTRAP FROM ARGUMENT-VALUE
END-IF
ACCEPT WS-ARG3 FROM ARGUMENT-VALUE
END-PERFORM.
SERVICE-CREATE.
* Build JSON payload for service creation
* Start with base payload containing name
STRING "curl -s -X POST "
"https://api.unsandbox.com/services "
"-H 'Content-Type: application/json' "
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
"' -d '{""name"":"""
FUNCTION TRIM(WS-NAME)
""""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
* Add ports if provided
IF WS-PORTS NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",""ports"":[" FUNCTION TRIM(WS-PORTS) "]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add domains if provided
IF WS-DOMAINS NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",""domains"":["""
FUNCTION TRIM(WS-DOMAINS)
"""]"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add service_type if provided
IF WS-SERVICE-TYPE NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",""service_type"":"""
FUNCTION TRIM(WS-SERVICE-TYPE)
""""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Add bootstrap if provided
IF WS-BOOTSTRAP NOT = SPACES
STRING FUNCTION TRIM(WS-CURL-CMD)
",""bootstrap"":"""
FUNCTION TRIM(WS-BOOTSTRAP)
""""
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING
END-IF.
* Close JSON and add output formatting
STRING FUNCTION TRIM(WS-CURL-CMD)
"}' | jq -r '.id + "" created""'"
DELIMITED BY SIZE INTO WS-CURL-CMD
END-STRING.
CALL "SYSTEM" USING WS-CURL-CMD.

8
un.cpp
View file

@ -217,7 +217,7 @@ void cmd_session(bool list, const string& kill, const string& shell, const strin
cout << exec_curl(cmd) << endl;
}
void cmd_service(const string& name, const string& ports, const string& bootstrap, bool list, const string& info, const string& logs, const string& tail, const string& sleep, const string& wake, const string& destroy, const string& network, int vcpu, const string& api_key) {
void cmd_service(const string& name, const string& ports, const string& type, const string& bootstrap, bool list, const string& info, const string& logs, const string& tail, const string& sleep, const string& wake, const string& destroy, const string& network, int vcpu, const string& api_key) {
if (list) {
string cmd = "curl -s -X GET '" + API_BASE + "/services' -H 'Authorization: Bearer " + api_key + "'";
cout << exec_curl(cmd) << endl;
@ -267,6 +267,7 @@ void cmd_service(const string& name, const string& ports, const string& bootstra
ostringstream json;
json << "{\"name\":\"" << name << "\"";
if (!ports.empty()) json << ",\"ports\":[" << ports << "]";
if (!type.empty()) json << ",\"service_type\":\"" << type << "\"";
if (!bootstrap.empty()) {
struct stat st;
if (stat(bootstrap.c_str(), &st) == 0) {
@ -328,7 +329,7 @@ int main(int argc, char* argv[]) {
}
if (cmd_type == "service") {
string name, ports, bootstrap;
string name, ports, type, bootstrap;
bool list = false;
string info, logs, tail, sleep, wake, destroy, network;
int vcpu = 0;
@ -337,6 +338,7 @@ int main(int argc, char* argv[]) {
string arg = argv[i];
if (arg == "--name" && i+1 < argc) name = argv[++i];
else if (arg == "--ports" && i+1 < argc) ports = argv[++i];
else if (arg == "--type" && i+1 < argc) type = argv[++i];
else if (arg == "--bootstrap" && i+1 < argc) bootstrap = argv[++i];
else if (arg == "--list") list = true;
else if (arg == "--info" && i+1 < argc) info = argv[++i];
@ -350,7 +352,7 @@ int main(int argc, char* argv[]) {
else if (arg == "-k" && i+1 < argc) api_key = argv[++i];
}
cmd_service(name, ports, bootstrap, list, info, logs, tail, sleep, wake, destroy, network, vcpu, api_key);
cmd_service(name, ports, type, bootstrap, list, info, logs, tail, sleep, wake, destroy, network, vcpu, api_key);
return 0;
}

60
un.cr
View file

@ -284,7 +284,53 @@ def cmd_service(args)
return
end
STDERR.puts "#{RED}Error: Use --list, --info, --logs, --sleep, --wake, or --destroy#{RESET}"
# Create new service
if name = args[:name]?.as?(String)
payload = JSON.parse({name: name}.to_json)
# Add ports
if ports_str = args[:ports]?.as?(String)
ports = ports_str.split(',').map(&.to_i)
payload.as_h["ports"] = JSON.parse(ports.to_json)
end
# Add domains
if domains_str = args[:domains]?.as?(String)
domains = domains_str.split(',')
payload.as_h["domains"] = JSON.parse(domains.to_json)
end
# Add service_type
if service_type = args[:service_type]?.as?(String)
payload.as_h["service_type"] = JSON::Any.new(service_type)
end
# Add bootstrap
if bootstrap = args[:bootstrap]?.as?(String)
# Check if bootstrap is a file
if File.exists?(bootstrap)
payload.as_h["bootstrap"] = JSON::Any.new(File.read(bootstrap))
else
payload.as_h["bootstrap"] = JSON::Any.new(bootstrap)
end
end
# Add network
if network = args[:network]?.as?(String)
payload.as_h["network"] = JSON::Any.new(network)
end
# Create service
result = api_request("/services", api_key, method: "POST", data: payload)
puts "#{GREEN}Service created: #{result["id"]?.try(&.as_s?) || "N/A"}#{RESET}"
puts "Name: #{result["name"]?.try(&.as_s?) || "N/A"}"
if url = result["url"]?.try(&.as_s?)
puts "URL: #{url}"
end
return
end
STDERR.puts "#{RED}Error: Use --list, --info, --logs, --sleep, --wake, --destroy, or --name to create#{RESET}"
exit 1
end
@ -304,7 +350,12 @@ def main
logs: nil,
sleep: nil,
wake: nil,
destroy: nil
destroy: nil,
name: nil,
ports: nil,
domains: nil,
service_type: nil,
bootstrap: nil
} of Symbol => (String | Array(String) | Bool | Nil)
parser = OptionParser.new do |opts|
@ -323,6 +374,11 @@ def main
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.on("--name=NAME", "Service name") { |n| args[:name] = n }
opts.on("--ports=PORTS", "Comma-separated ports") { |p| args[:ports] = p }
opts.on("--domains=DOMAINS", "Comma-separated domains") { |d| args[:domains] = d }
opts.on("--type=TYPE", "Service type for SRV records") { |t| args[:service_type] = t }
opts.on("--bootstrap=CMD", "Bootstrap command/file") { |b| args[:bootstrap] = b }
opts.unknown_args do |before, after|
if before.size > 0

8
un.d
View file

@ -151,7 +151,7 @@ void cmdSession(bool list, string kill, string shell, string network, int vcpu,
writeln(execCurl(cmd));
}
void cmdService(string name, string ports, string bootstrap, bool list, string info, string logs, string tail, string sleep, string wake, string destroy, string network, int vcpu, string apiKey) {
void cmdService(string name, string ports, string bootstrap, string type, bool list, string info, string logs, string tail, string sleep, string wake, string destroy, string network, int vcpu, string apiKey) {
if (list) {
string cmd = format(`curl -s -X GET '%s/services' -H 'Authorization: Bearer %s'`, API_BASE, apiKey);
writeln(execCurl(cmd));
@ -200,6 +200,7 @@ void cmdService(string name, string ports, string bootstrap, bool list, string i
if (!name.empty) {
string json = format(`{"name":"%s"`, name);
if (!ports.empty) json ~= format(`,"ports":[%s]`, ports);
if (!type.empty) json ~= format(`,"service_type":"%s"`, type);
if (!bootstrap.empty) {
if (exists(bootstrap)) {
string bootCode = readText(bootstrap);
@ -254,7 +255,7 @@ int main(string[] args) {
}
if (args[1] == "service") {
string name, ports, bootstrap;
string name, ports, bootstrap, type;
bool list = false;
string info, logs, tail, sleep, wake, destroy, network;
int vcpu = 0;
@ -263,6 +264,7 @@ int main(string[] args) {
if (args[i] == "--name" && i+1 < args.length) name = args[++i];
else if (args[i] == "--ports" && i+1 < args.length) ports = args[++i];
else if (args[i] == "--bootstrap" && i+1 < args.length) bootstrap = args[++i];
else if (args[i] == "--type" && i+1 < args.length) type = args[++i];
else if (args[i] == "--list") list = true;
else if (args[i] == "--info" && i+1 < args.length) info = args[++i];
else if (args[i] == "--logs" && i+1 < args.length) logs = args[++i];
@ -275,7 +277,7 @@ int main(string[] args) {
else if (args[i] == "-k" && i+1 < args.length) apiKey = args[++i];
}
cmdService(name, ports, bootstrap, list, info, logs, tail, sleep, wake, destroy, network, vcpu, apiKey);
cmdService(name, ports, bootstrap, type, list, info, logs, tail, sleep, wake, destroy, network, vcpu, apiKey);
return 0;
}

View file

@ -82,6 +82,7 @@ class Args {
bool serviceList = false;
String? serviceName;
String? servicePorts;
String? serviceType;
String? serviceBootstrap;
String? serviceInfo;
String? serviceLogs;
@ -321,6 +322,9 @@ Future<void> cmdService(Args args) async {
if (args.servicePorts != null) {
payload['ports'] = args.servicePorts!.split(',').map((p) => int.parse(p.trim())).toList();
}
if (args.serviceType != null) {
payload['service_type'] = args.serviceType;
}
if (args.serviceBootstrap != null) {
payload['bootstrap'] = args.serviceBootstrap;
}
@ -404,6 +408,9 @@ Args parseArgs(List<String> argv) {
case '--ports':
args.servicePorts = argv[++i];
break;
case '--type':
args.serviceType = argv[++i];
break;
case '--bootstrap':
args.serviceBootstrap = argv[++i];
break;
@ -459,6 +466,7 @@ Service options:
--list List services
--name NAME Service name
--ports PORTS Comma-separated ports
--type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)
--bootstrap CMD Bootstrap command
--info ID Get service details
--logs ID Get all logs

11
un.erl
View file

@ -152,6 +152,7 @@ service_command(Args) ->
ApiKey = get_api_key(),
Ports = get_service_ports(Args),
Bootstrap = get_service_bootstrap(Args),
Type = get_service_type(Args),
PortsJson = case Ports of
undefined -> "";
P -> ",\"ports\":[" ++ P ++ "]"
@ -160,7 +161,11 @@ service_command(Args) ->
undefined -> "";
B -> ",\"bootstrap\":\"" ++ escape_json(B) ++ "\""
end,
Json = "{\"name\":\"" ++ Name ++ "\"" ++ PortsJson ++ BootstrapJson ++ "}",
TypeJson = case Type of
undefined -> "";
T -> ",\"service_type\":\"" ++ T ++ "\""
end,
Json = "{\"name\":\"" ++ Name ++ "\"" ++ PortsJson ++ BootstrapJson ++ TypeJson ++ "}",
TmpFile = write_temp_file(Json),
Response = curl_post(ApiKey, "/services", TmpFile),
file:delete(TmpFile),
@ -280,3 +285,7 @@ get_service_ports([_ | Rest]) -> get_service_ports(Rest).
get_service_bootstrap([]) -> undefined;
get_service_bootstrap(["--bootstrap", Bootstrap | _]) -> Bootstrap;
get_service_bootstrap([_ | Rest]) -> get_service_bootstrap(Rest).
get_service_type([]) -> undefined;
get_service_type(["--type", Type | _]) -> Type;
get_service_type([_ | Rest]) -> get_service_type(Rest).

4
un.ex
View file

@ -193,13 +193,15 @@ defmodule Un do
bootstrap = get_opt(args, "--bootstrap", nil, nil)
network = get_opt(args, "-n", nil, nil)
vcpu = get_opt(args, "-v", nil, nil)
service_type = get_opt(args, "--type", nil, nil)
ports_json = if ports, do: ",\"ports\":[#{ports}]", else: ""
bootstrap_json = if bootstrap, do: ",\"bootstrap\":\"#{escape_json(bootstrap)}\"", else: ""
network_json = if network, do: ",\"network\":\"#{network}\"", else: ""
vcpu_json = if vcpu, do: ",\"vcpu\":#{vcpu}", else: ""
type_json = if service_type, do: ",\"service_type\":\"#{service_type}\"", else: ""
json = "{\"name\":\"#{name}\"#{ports_json}#{bootstrap_json}#{network_json}#{vcpu_json}}"
json = "{\"name\":\"#{name}\"#{ports_json}#{bootstrap_json}#{network_json}#{vcpu_json}#{type_json}}"
response = curl_post(api_key, "/services", json)
IO.puts("#{@green}Service created#{@reset}")
IO.puts(response)

7
un.f90
View file

@ -214,19 +214,24 @@ contains
subroutine handle_service()
character(len=2048) :: full_cmd
character(len=256) :: arg, service_id, operation
character(len=256) :: arg, service_id, operation, service_type
integer :: i, stat
logical :: list_mode
list_mode = .false.
operation = ''
service_id = ''
service_type = ''
! Parse service arguments
do i = 2, command_argument_count()
call get_command_argument(i, arg)
if (trim(arg) == '-l' .or. trim(arg) == '--list') then
list_mode = .true.
else if (trim(arg) == '--type') then
if (i+1 <= command_argument_count()) then
call get_command_argument(i+1, service_type)
end if
else if (trim(arg) == '--info') then
operation = 'info'
if (i+1 <= command_argument_count()) then

View file

@ -252,6 +252,36 @@
s" chmod +x /tmp/unsandbox_cmd.sh && /tmp/unsandbox_cmd.sh && rm -f /tmp/unsandbox_cmd.sh" system
;
\ Service create (requires --name, optional --ports, --domains, --type, --bootstrap)
: service-create ( -- )
get-api-key
\ Parse arguments (simplified - in real implementation would iterate through args)
\ For now, just create the curl command that will be constructed by bash
s" /tmp/unsandbox_cmd.sh" w/o create-file throw >r
s" #!/bin/bash" r@ write-line throw
s" NAME=''; PORTS=''; DOMAINS=''; TYPE=''; BOOTSTRAP=''" r@ write-line throw
s" for ((i=3; i<$#; i+=2)); do" r@ write-line throw
s" case ${!i} in" r@ write-line throw
s" --name) NAME=${!((i+1))} ;;" r@ write-line throw
s" --ports) PORTS=${!((i+1))} ;;" r@ write-line throw
s" --domains) DOMAINS=${!((i+1))} ;;" r@ write-line throw
s" --type) TYPE=${!((i+1))} ;;" r@ write-line throw
s" --bootstrap) BOOTSTRAP=${!((i+1))} ;;" r@ write-line throw
s" esac" r@ write-line throw
s" done" r@ write-line throw
s" [ -z \"$NAME\" ] && echo 'Error: --name required' && exit 1" r@ write-line throw
s" PAYLOAD='{\"name\":\"'\"$NAME\"'\"}'" r@ write-line throw
s" [ -n \"$PORTS\" ] && PAYLOAD=$(echo $PAYLOAD | jq --arg p \"$PORTS\" '. + {ports: ($p | split(\",\") | map(tonumber))}')" r@ write-line throw
s" [ -n \"$DOMAINS\" ] && PAYLOAD=$(echo $PAYLOAD | jq --arg d \"$DOMAINS\" '. + {domains: ($d | split(\",\"))}')" r@ write-line throw
s" [ -n \"$TYPE\" ] && PAYLOAD=$(echo $PAYLOAD | jq --arg t \"$TYPE\" '. + {service_type: $t}')" r@ write-line throw
s" [ -n \"$BOOTSTRAP\" ] && PAYLOAD=$(echo $PAYLOAD | jq --arg b \"$BOOTSTRAP\" '. + {bootstrap: $b}')" r@ write-line throw
s" curl -s -X POST https://api.unsandbox.com/services -H 'Content-Type: application/json' -H 'Authorization: Bearer " r@ write-file throw
get-api-key r@ write-file throw
s" ' -d \"$PAYLOAD\" | jq ." r@ write-line throw
r> close-file throw
s" chmod +x /tmp/unsandbox_cmd.sh && bash /tmp/unsandbox_cmd.sh && rm -f /tmp/unsandbox_cmd.sh" system
;
\ Handle session subcommand
: handle-session ( -- )
argc @ 3 < if
@ -287,7 +317,7 @@
\ Handle service subcommand
: handle-service ( -- )
argc @ 3 < if
s" Error: Use --list, --info, --logs, --sleep, --wake, or --destroy" type cr
s" Error: Use --name (create), --list, --info, --logs, --sleep, --wake, or --destroy" type cr
1 (bye)
then
@ -301,6 +331,11 @@
0 (bye)
then
2dup s" --name" compare 0= if
2drop service-create
0 (bye)
then
2dup s" --info" compare 0= if
2drop
argc @ 4 < if
@ -352,7 +387,7 @@
then
2drop
s" Error: Use --list, --info, --logs, --sleep, --wake, or --destroy" type cr
s" Error: Use --name (create), --list, --info, --logs, --sleep, --wake, or --destroy" type cr
1 (bye)
;

6
un.fs
View file

@ -85,6 +85,7 @@ type Args = {
mutable ServiceList: bool
mutable ServiceName: string option
mutable ServicePorts: string option
mutable ServiceType: string option
mutable ServiceBootstrap: string option
mutable ServiceInfo: string option
mutable ServiceLogs: string option
@ -357,6 +358,8 @@ let cmdService (args: Args) =
if args.ServicePorts.IsSome then
let ports = args.ServicePorts.Value.Split(',') |> Array.map (fun p -> box (int (p.Trim())))
payload <- payload @ [("ports", box ports)]
if args.ServiceType.IsSome then
payload <- payload @ [("service_type", box args.ServiceType.Value)]
if args.ServiceBootstrap.IsSome then
payload <- payload @ [("bootstrap", box args.ServiceBootstrap.Value)]
if args.Network.IsSome then
@ -395,6 +398,7 @@ let parseArgs (argv: string[]) =
ServiceList = false
ServiceName = None
ServicePorts = None
ServiceType = None
ServiceBootstrap = None
ServiceInfo = None
ServiceLogs = None
@ -425,6 +429,7 @@ let parseArgs (argv: string[]) =
| "--kill" -> i <- i + 1; args.SessionKill <- Some argv.[i]
| "--name" -> i <- i + 1; args.ServiceName <- Some argv.[i]
| "--ports" -> i <- i + 1; args.ServicePorts <- Some argv.[i]
| "--type" -> i <- i + 1; args.ServiceType <- Some argv.[i]
| "--bootstrap" -> i <- i + 1; args.ServiceBootstrap <- Some argv.[i]
| "--info" -> i <- i + 1; args.ServiceInfo <- Some argv.[i]
| "--logs" -> i <- i + 1; args.ServiceLogs <- Some argv.[i]
@ -461,6 +466,7 @@ let printHelp () =
printfn " --list List services"
printfn " --name NAME Service name"
printfn " --ports PORTS Comma-separated ports"
printfn " --type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)"
printfn " --bootstrap CMD Bootstrap command"
printfn " --info ID Get service details"
printfn " --logs ID Get all logs"

8
un.go
View file

@ -338,7 +338,7 @@ func cmdSession(sessionList, sessionKill, sessionShell, network string, vcpu int
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) {
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{})
@ -422,6 +422,9 @@ func cmdService(serviceName, servicePorts, serviceDomains, serviceBootstrap, ser
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 {
@ -481,6 +484,7 @@ func main() {
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")
@ -523,7 +527,7 @@ func main() {
if vc == 0 {
vc = *vcpu
}
cmdService(*serviceName, *servicePorts, *serviceDomains, *serviceBootstrap, *serviceList, *serviceInfo, *serviceLogs, *serviceTail, *serviceSleep, *serviceWake, *serviceDestroy, net, vc, key)
cmdService(*serviceName, *servicePorts, *serviceDomains, *serviceType, *serviceBootstrap, *serviceList, *serviceInfo, *serviceLogs, *serviceTail, *serviceSleep, *serviceWake, *serviceDestroy, net, vc, key)
return
}
}

View file

@ -76,6 +76,7 @@ class Args {
Boolean serviceList = false
String serviceName = null
String servicePorts = null
String serviceType = null
String serviceBootstrap = null
String serviceInfo = null
String serviceLogs = null
@ -331,6 +332,9 @@ def cmdService(args) {
def ports = args.servicePorts.split(',').collect { it.trim() }.join(',')
json += ""","ports":[${ports}]"""
}
if (args.serviceType) {
json += ""","service_type":"${args.serviceType}""""
}
if (args.serviceBootstrap) {
def escaped = args.serviceBootstrap.replace('\\', '\\\\').replace('"', '\\"')
json += ""","bootstrap":"${escaped}""""
@ -420,6 +424,9 @@ def parseArgs(argv) {
case '--ports':
args.servicePorts = argv[++i]
break
case '--type':
args.serviceType = argv[++i]
break
case '--bootstrap':
args.serviceBootstrap = argv[++i]
break
@ -474,6 +481,7 @@ Service options:
--list List services
--name NAME Service name
--ports PORTS Comma-separated ports
--type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)
--bootstrap CMD Bootstrap command
--info ID Get service details
--logs ID Get all logs

7
un.hs
View file

@ -130,6 +130,7 @@ data ServiceOpts = ServiceOpts
{ svcAction :: ServiceAction
, svcName :: Maybe String
, svcPorts :: Maybe String
, svcType :: Maybe String
, svcBootstrap :: Maybe String
, svcNetwork :: Maybe String
, svcVcpu :: Maybe Int
@ -161,7 +162,7 @@ parseSession args = return $ parseSessionArgs args defaultSessionOpts
parseService :: [String] -> IO ServiceOpts
parseService args = return $ parseServiceArgs args defaultServiceOpts
where
defaultServiceOpts = ServiceOpts ServiceCreate Nothing Nothing Nothing Nothing Nothing
defaultServiceOpts = ServiceOpts ServiceCreate Nothing Nothing Nothing Nothing Nothing Nothing
parseServiceArgs [] opts = opts
parseServiceArgs ("--list":rest) opts = parseServiceArgs rest opts { svcAction = ServiceList }
parseServiceArgs ("--info":id:rest) opts = parseServiceArgs rest opts { svcAction = ServiceInfo id }
@ -171,6 +172,7 @@ parseService args = return $ parseServiceArgs args defaultServiceOpts
parseServiceArgs ("--destroy":id:rest) opts = parseServiceArgs rest opts { svcAction = ServiceDestroy id }
parseServiceArgs ("--name":n:rest) opts = parseServiceArgs rest opts { svcName = Just n }
parseServiceArgs ("--ports":p:rest) opts = parseServiceArgs rest opts { svcPorts = Just p }
parseServiceArgs ("--type":t:rest) opts = parseServiceArgs rest opts { svcType = Just t }
parseServiceArgs ("--bootstrap":b:rest) opts = parseServiceArgs rest opts { svcBootstrap = Just b }
parseServiceArgs ("-n":net:rest) opts = parseServiceArgs rest opts { svcNetwork = Just net }
parseServiceArgs ("-v":v:rest) opts = parseServiceArgs rest opts { svcVcpu = Just (read v) }
@ -317,10 +319,11 @@ serviceCommand opts = do
exitFailure
Just name -> do
let portsJSON = maybe "" (\p -> ",\"ports\":[" ++ p ++ "]") (svcPorts opts)
let typeJSON = maybe "" (\t -> ",\"service_type\":\"" ++ t ++ "\"") (svcType opts)
let bootstrapJSON = maybe "" (\b -> ",\"bootstrap\":\"" ++ escapeJSON b ++ "\"") (svcBootstrap opts)
let networkJSON = maybe "" (\n -> ",\"network\":\"" ++ n ++ "\"") (svcNetwork opts)
let vcpuJSON = maybe "" (\v -> ",\"vcpu\":" ++ show v) (svcVcpu opts)
let json = "{\"name\":\"" ++ name ++ "\"" ++ portsJSON ++ bootstrapJSON ++ networkJSON ++ vcpuJSON ++ "}"
let json = "{\"name\":\"" ++ name ++ "\"" ++ portsJSON ++ typeJSON ++ bootstrapJSON ++ networkJSON ++ vcpuJSON ++ "}"
(_, stdout, _) <- curlPost apiKey "/services" json
putStrLn $ green ++ "Service created" ++ reset
putStrLn stdout

64
un.jl
View file

@ -286,7 +286,51 @@ function cmd_service(args)
return
end
println(stderr, "$(RED)Error: Use --list, --info, --logs, --sleep, --wake, or --destroy$(RESET)")
# Create new service
if args["name"] !== nothing
payload = Dict("name" => args["name"])
if args["ports"] !== nothing
ports = [parse(Int, strip(p)) for p in split(args["ports"], ',')]
payload["ports"] = ports
end
if args["domains"] !== nothing
domains = [strip(d) for d in split(args["domains"], ',')]
payload["domains"] = domains
end
if args["type"] !== nothing
payload["service_type"] = args["type"]
end
if args["bootstrap"] !== nothing
bootstrap = args["bootstrap"]
if isfile(bootstrap)
payload["bootstrap"] = read(bootstrap, String)
else
payload["bootstrap"] = bootstrap
end
end
if args["network"] !== nothing
payload["network"] = args["network"]
end
if args["vcpu"] !== nothing
payload["vcpu"] = args["vcpu"]
end
result = api_request("/services", api_key, method="POST", data=payload)
println("$(GREEN)Service created: $(get(result, "id", "N/A"))$(RESET)")
println("Name: $(get(result, "name", "N/A"))")
if haskey(result, "url")
println("URL: $(result["url"])")
end
return
end
println(stderr, "$(RED)Error: Use --name to create, or --list, --info, --logs, --sleep, --wake, --destroy$(RESET)")
exit(1)
end
@ -333,6 +377,24 @@ function main()
end
@add_arg_table! s["service"] begin
"--name"
help = "Service name"
"--ports"
help = "Comma-separated ports"
"--domains"
help = "Comma-separated custom domains"
"--type"
help = "Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)"
"--bootstrap"
help = "Bootstrap command/file"
"--network", "-n"
help = "Network mode"
arg_type = String
range_tester = x -> x in ["zerotrust", "semitrusted"]
"--vcpu", "-v"
help = "vCPU count (1-8)"
arg_type = Int
range_tester = x -> x >= 1 && x <= 8
"--list", "-l"
help = "List services"
action = :store_true

5
un.js
View file

@ -325,6 +325,7 @@ async function cmdService(args) {
const payload = { name: args.name };
if (args.ports) payload.ports = args.ports.split(',').map(p => parseInt(p.trim()));
if (args.domains) payload.domains = args.domains.split(',');
if (args.serviceType) payload.service_type = args.serviceType;
if (args.bootstrap) {
if (fs.existsSync(args.bootstrap)) {
payload.bootstrap = fs.readFileSync(args.bootstrap, 'utf-8');
@ -436,6 +437,9 @@ function parseArgs(argv) {
} else if (arg === '--domains' && i + 1 < argv.length) {
args.domains = argv[++i];
i++;
} else if (arg === '--type' && i + 1 < argv.length) {
args.serviceType = argv[++i];
i++;
} else if (arg === '--bootstrap' && i + 1 < argv.length) {
args.bootstrap = argv[++i];
i++;
@ -514,6 +518,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

6
un.kt
View file

@ -85,6 +85,7 @@ data class Args(
var serviceList: Boolean = false,
var serviceName: String? = null,
var servicePorts: String? = null,
var serviceType: String? = null,
var serviceBootstrap: String? = null,
var serviceInfo: String? = null,
var serviceLogs: String? = null,
@ -302,6 +303,9 @@ fun cmdService(args: Args) {
if (args.servicePorts != null) {
payload["ports"] = args.servicePorts!!.split(",").map { it.trim().toInt() }
}
if (args.serviceType != null) {
payload["service_type"] = args.serviceType!!
}
if (args.serviceBootstrap != null) {
payload["bootstrap"] = args.serviceBootstrap!!
}
@ -513,6 +517,7 @@ fun parseArgs(args: Array<String>): Args {
"--kill" -> result.sessionKill = args[++i]
"--name" -> result.serviceName = args[++i]
"--ports" -> result.servicePorts = args[++i]
"--type" -> result.serviceType = args[++i]
"--bootstrap" -> result.serviceBootstrap = args[++i]
"--info" -> result.serviceInfo = args[++i]
"--logs" -> result.serviceLogs = args[++i]
@ -551,6 +556,7 @@ Service options:
--list List services
--name NAME Service name
--ports PORTS Comma-separated ports
--type TYPE Service type (minecraft/mumble/teamspeak/source/tcp/udp)
--bootstrap CMD Bootstrap command
--info ID Get service details
--logs ID Get all logs

36
un.lisp
View file

@ -153,7 +153,7 @@
(format t "~aSession created (WebSocket required)~a~%" *yellow* *reset*)
(format t "~a~%" response))))))
(defun service-cmd (action id name ports bootstrap)
(defun service-cmd (action id name ports bootstrap service-type)
(let ((api-key (get-api-key)))
(cond
((string= action "list")
@ -174,7 +174,8 @@
((and (string= action "create") name)
(let* ((ports-json (if ports (format nil ",\"ports\":[~a]" ports) ""))
(bootstrap-json (if bootstrap (format nil ",\"bootstrap\":\"~a\"" (escape-json bootstrap)) ""))
(json (format nil "{\"name\":\"~a\"~a~a}" name ports-json bootstrap-json))
(type-json (if service-type (format nil ",\"service_type\":\"~a\"" service-type) ""))
(json (format nil "{\"name\":\"~a\"~a~a~a}" name ports-json bootstrap-json type-json))
(response (curl-post api-key "/services" json)))
(format t "~aService created~a~%" *green* *reset*)
(format t "~a~%" response)))
@ -202,24 +203,31 @@
((string= (first args) "service")
(cond
((and (> (length args) 1) (string= (second args) "--list"))
(service-cmd "list" nil nil nil nil))
(service-cmd "list" nil nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--info"))
(service-cmd "info" (third args) nil nil nil))
(service-cmd "info" (third args) nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--logs"))
(service-cmd "logs" (third args) nil nil nil))
(service-cmd "logs" (third args) nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--sleep"))
(service-cmd "sleep" (third args) nil nil nil))
(service-cmd "sleep" (third args) nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--wake"))
(service-cmd "wake" (third args) nil nil nil))
(service-cmd "wake" (third args) nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--destroy"))
(service-cmd "destroy" (third args) nil nil nil))
(service-cmd "destroy" (third args) nil nil nil nil))
((and (> (length args) 2) (string= (second args) "--name"))
(let ((name (third args))
(ports (when (and (> (length args) 4) (string= (fourth args) "--ports"))
(fifth args)))
(bootstrap (when (and (> (length args) 6) (string= (sixth args) "--bootstrap"))
(seventh args))))
(service-cmd "create" nil name ports bootstrap)))
(let* ((name (third args))
(rest-args (nthcdr 3 args))
(ports nil)
(bootstrap nil)
(service-type nil))
(loop for i from 0 below (length rest-args) by 2
do (let ((opt (nth i rest-args))
(val (nth (1+ i) rest-args)))
(cond
((string= opt "--ports") (setf ports val))
((string= opt "--bootstrap") (setf bootstrap val))
((string= opt "--type") (setf service-type val)))))
(service-cmd "create" nil name ports bootstrap service-type)))
(t
(format t "Error: Invalid service command~%")
(uiop:quit 1))))

8
un.lua
View file

@ -384,6 +384,9 @@ local function cmd_service(options)
end
payload.domains = domains
end
if options.type then
payload.service_type = options.type
end
if options.bootstrap then
local file = io.open(options.bootstrap, "r")
if file then
@ -428,6 +431,7 @@ local function main()
name = nil,
ports = nil,
domains = nil,
type = nil,
bootstrap = nil,
info = nil,
logs = nil,
@ -491,6 +495,9 @@ local function main()
elseif a == "--domains" then
i = i + 1
options.domains = arg[i]
elseif a == "--type" then
i = i + 1
options.type = arg[i]
elseif a == "--bootstrap" then
i = i + 1
options.bootstrap = arg[i]
@ -562,6 +569,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type (minecraft|mumble|teamspeak|source|tcp|udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

7
un.m
View file

@ -336,6 +336,7 @@ void cmdService(NSArray* args) {
NSString* destroyId = nil;
NSString* name = nil;
NSString* ports = nil;
NSString* type = nil;
NSString* bootstrap = nil;
NSString* network = nil;
int vcpu = 0;
@ -359,6 +360,8 @@ void cmdService(NSArray* args) {
name = args[++i];
} else if ([arg isEqualToString:@"--ports"] && i + 1 < [args count]) {
ports = args[++i];
} else if ([arg isEqualToString:@"--type"] && i + 1 < [args count]) {
type = args[++i];
} else if ([arg isEqualToString:@"--bootstrap"] && i + 1 < [args count]) {
bootstrap = args[++i];
} else if ([arg isEqualToString:@"-n"] && i + 1 < [args count]) {
@ -441,6 +444,10 @@ void cmdService(NSArray* args) {
payload[@"ports"] = portNumbers;
}
if (type) {
payload[@"service_type"] = type;
}
if (bootstrap) {
NSFileManager* fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath:bootstrap]) {

36
un.ml
View file

@ -265,7 +265,7 @@ let session_command action shell network vcpu =
| _ -> ()
(* Service command *)
let service_command action name ports bootstrap network vcpu =
let service_command action name ports bootstrap service_type network vcpu =
let api_key = get_api_key () in
match action with
| "list" ->
@ -330,9 +330,10 @@ let service_command action name ports bootstrap network vcpu =
| Some n ->
let ports_json = match ports with Some p -> Printf.sprintf ",\"ports\":[%s]" p | None -> "" in
let bootstrap_json = match bootstrap with Some b -> Printf.sprintf ",\"bootstrap\":\"%s\"" (escape_json b) | None -> "" in
let service_type_json = match service_type with Some t -> Printf.sprintf ",\"service_type\":\"%s\"" t | None -> "" in
let network_json = match network with Some net -> Printf.sprintf ",\"network\":\"%s\"" net | None -> "" in
let vcpu_json = match vcpu with Some v -> Printf.sprintf ",\"vcpu\":%d" v | None -> "" in
let json = Printf.sprintf "{\"name\":\"%s\"%s%s%s%s}" n ports_json bootstrap_json network_json vcpu_json in
let json = Printf.sprintf "{\"name\":\"%s\"%s%s%s%s%s}" n ports_json bootstrap_json service_type_json network_json vcpu_json in
let tmp_file = Printf.sprintf "/tmp/un_ocaml_%d.json" (Random.int 999999) in
let oc = open_out tmp_file in
output_string oc json;
@ -376,22 +377,23 @@ let () =
in
parse_session "create" None None None rest
| "service" :: rest ->
let rec parse_service action name ports bootstrap network vcpu = function
| [] -> service_command action name ports bootstrap network vcpu
| "--list" :: rest -> parse_service "list" name ports bootstrap network vcpu rest
| "--info" :: id :: rest -> parse_service "info" (Some id) ports bootstrap network vcpu rest
| "--logs" :: id :: rest -> parse_service "logs" (Some id) ports bootstrap network vcpu rest
| "--sleep" :: id :: rest -> parse_service "sleep" (Some id) ports bootstrap network vcpu rest
| "--wake" :: id :: rest -> parse_service "wake" (Some id) ports bootstrap network vcpu rest
| "--destroy" :: id :: rest -> parse_service "destroy" (Some id) ports bootstrap network vcpu rest
| "--name" :: n :: rest -> parse_service "create" (Some n) ports bootstrap network vcpu rest
| "--ports" :: p :: rest -> parse_service action name (Some p) bootstrap network vcpu rest
| "--bootstrap" :: b :: rest -> parse_service action name ports (Some b) network vcpu rest
| "-n" :: net :: rest -> parse_service action name ports bootstrap (Some net) vcpu rest
| "-v" :: v :: rest -> parse_service action name ports bootstrap network (Some (int_of_string v)) rest
| _ :: rest -> parse_service action name ports bootstrap network vcpu rest
let rec parse_service action name ports bootstrap service_type network vcpu = function
| [] -> service_command action name ports bootstrap service_type network vcpu
| "--list" :: rest -> parse_service "list" name ports bootstrap service_type network vcpu rest
| "--info" :: id :: rest -> parse_service "info" (Some id) ports bootstrap service_type network vcpu rest
| "--logs" :: id :: rest -> parse_service "logs" (Some id) ports bootstrap service_type network vcpu rest
| "--sleep" :: id :: rest -> parse_service "sleep" (Some id) ports bootstrap service_type network vcpu rest
| "--wake" :: id :: rest -> parse_service "wake" (Some id) ports bootstrap service_type network vcpu rest
| "--destroy" :: id :: rest -> parse_service "destroy" (Some id) ports bootstrap service_type network vcpu rest
| "--name" :: n :: rest -> parse_service "create" (Some n) ports bootstrap service_type network vcpu rest
| "--ports" :: p :: rest -> parse_service action name (Some p) bootstrap service_type network vcpu rest
| "--bootstrap" :: b :: rest -> parse_service action name ports (Some b) service_type network vcpu rest
| "--type" :: t :: rest -> parse_service action name ports bootstrap (Some t) network vcpu rest
| "-n" :: net :: rest -> parse_service action name ports bootstrap service_type (Some net) vcpu rest
| "-v" :: v :: rest -> parse_service action name ports bootstrap service_type network (Some (int_of_string v)) rest
| _ :: rest -> parse_service action name ports bootstrap service_type network vcpu rest
in
parse_service "create" None None None None None rest
parse_service "create" None None None None None None rest
| args ->
let rec parse_execute file env_vars artifacts out_dir network vcpu = function
| [] -> execute_command file env_vars artifacts out_dir network vcpu

8
un.nim
View file

@ -127,7 +127,7 @@ proc cmdSession(list: bool, kill, shell, network: string, vcpu: int, tmux, scree
let cmd = fmt"""curl -s -X POST '{API_BASE}/sessions' -H 'Content-Type: application/json' -H 'Authorization: Bearer {apiKey}' -d '{json}'"""
echo execCurl(cmd)
proc cmdService(name, ports, bootstrap: string, list: bool, info, logs, tail, sleep, wake, destroy, network: string, vcpu: int, apiKey: string) =
proc cmdService(name, ports, bootstrap, serviceType: string, list: bool, info, logs, tail, sleep, wake, destroy, network: string, vcpu: int, apiKey: string) =
if list:
let cmd = fmt"""curl -s -X GET '{API_BASE}/services' -H 'Authorization: Bearer {apiKey}'"""
echo execCurl(cmd)
@ -175,6 +175,7 @@ proc cmdService(name, ports, bootstrap: string, list: bool, info, logs, tail, sl
json.add(fmt""","bootstrap":"{escapeJson(bootCode)}"""")
else:
json.add(fmt""","bootstrap":"{escapeJson(bootstrap)}"""")
if serviceType != "": json.add(fmt""","service_type":"{serviceType}"""")
if network != "": json.add(fmt""","network":"{network}"""")
if vcpu > 0: json.add(fmt""","vcpu":{vcpu}""")
json.add("}")
@ -218,7 +219,7 @@ proc main() =
return
if args[0] == "service":
var name, ports, bootstrap = ""
var name, ports, bootstrap, serviceType = ""
var list = false
var info, logs, tail, sleep, wake, destroy, network = ""
var vcpu = 0
@ -228,6 +229,7 @@ proc main() =
of "--name": name = args[i+1]; inc i
of "--ports": ports = args[i+1]; inc i
of "--bootstrap": bootstrap = args[i+1]; inc i
of "--type": serviceType = args[i+1]; inc i
of "--list": list = true
of "--info": info = args[i+1]; inc i
of "--logs": logs = args[i+1]; inc i
@ -239,7 +241,7 @@ proc main() =
of "-v": vcpu = parseInt(args[i+1]); inc i
of "-k": apiKey = args[i+1]; inc i
inc i
cmdService(name, ports, bootstrap, list, info, logs, tail, sleep, wake, destroy, network, vcpu, apiKey)
cmdService(name, ports, bootstrap, serviceType, list, info, logs, tail, sleep, wake, destroy, network, vcpu, apiKey)
return
# Execute mode

8
un.php
View file

@ -336,6 +336,9 @@ function cmd_service($options) {
if ($options['domains']) {
$payload['domains'] = explode(',', $options['domains']);
}
if ($options['type']) {
$payload['service_type'] = $options['type'];
}
if ($options['bootstrap']) {
if (file_exists($options['bootstrap'])) {
$payload['bootstrap'] = file_get_contents($options['bootstrap']);
@ -380,6 +383,7 @@ function main() {
'name' => null,
'ports' => null,
'domains' => null,
'type' => null,
'bootstrap' => null,
'info' => null,
'logs' => null,
@ -452,6 +456,9 @@ function main() {
case '--domains':
$options['domains'] = $argv[++$i];
break;
case '--type':
$options['type'] = $argv[++$i];
break;
case '--bootstrap':
$options['bootstrap'] = $argv[++$i];
break;
@ -523,6 +530,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type (minecraft|mumble|teamspeak|source|tcp|udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

7
un.pl
View file

@ -334,6 +334,9 @@ sub cmd_service {
my @domains = split(',', $options->{domains});
$payload->{domains} = \@domains;
}
if ($options->{type}) {
$payload->{service_type} = $options->{type};
}
if ($options->{bootstrap}) {
if (-e $options->{bootstrap}) {
open my $fh, '<', $options->{bootstrap} or die "Cannot read file: $!";
@ -379,6 +382,7 @@ sub main {
name => undef,
ports => undef,
domains => undef,
type => undef,
bootstrap => undef,
info => undef,
logs => undef,
@ -429,6 +433,8 @@ sub main {
$options{ports} = $ARGV[++$i];
} elsif ($arg eq '--domains') {
$options{domains} = $ARGV[++$i];
} elsif ($arg eq '--type') {
$options{type} = $ARGV[++$i];
} elsif ($arg eq '--bootstrap') {
$options{bootstrap} = $ARGV[++$i];
} elsif ($arg eq '--info') {
@ -489,6 +495,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type (minecraft|mumble|teamspeak|source|tcp|udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

70
un.pro
View file

@ -175,6 +175,29 @@ service_destroy(ServiceId) :-
[ServiceId, ApiKey, ServiceId]),
shell(Cmd, 0).
% Service create
service_create(Name, Ports, Bootstrap, ServiceType) :-
get_api_key(ApiKey),
% Build JSON payload
( Ports \= ''
-> format(atom(PortsJson), ',"ports":[~w]', [Ports])
; PortsJson = ''
),
( Bootstrap \= ''
-> format(atom(BootstrapJson), ',"bootstrap":"~w"', [Bootstrap])
; BootstrapJson = ''
),
( ServiceType \= ''
-> format(atom(ServiceTypeJson), ',"service_type":"~w"', [ServiceType])
; ServiceTypeJson = ''
),
format(atom(Json), '{"name":"~w"~w~w~w}', [Name, PortsJson, BootstrapJson, ServiceTypeJson]),
% Execute curl command
format(atom(Cmd),
'curl -s -X POST https://api.unsandbox.com/services -H "Content-Type: application/json" -H "Authorization: Bearer ~w" -d \'~w\' && echo -e "\\x1b[32mService created\\x1b[0m"',
[ApiKey, Json]),
shell(Cmd, 0).
% Handle session subcommand
handle_session(['--list'|_]) :- session_list.
handle_session(['-l'|_]) :- session_list.
@ -184,16 +207,43 @@ handle_session(_) :-
halt(1).
% Handle service subcommand
handle_service(['--list'|_]) :- service_list.
handle_service(['-l'|_]) :- service_list.
handle_service(['--info', ServiceId|_]) :- service_info(ServiceId).
handle_service(['--logs', ServiceId|_]) :- service_logs(ServiceId).
handle_service(['--sleep', ServiceId|_]) :- service_sleep(ServiceId).
handle_service(['--wake', ServiceId|_]) :- service_wake(ServiceId).
handle_service(['--destroy', ServiceId|_]) :- service_destroy(ServiceId).
handle_service(_) :-
write(user_error, 'Error: Use --list, --info, --logs, --sleep, --wake, or --destroy\n'),
halt(1).
handle_service(Args) :-
parse_service_args(Args, '', '', '', '', Action),
execute_service_action(Action).
% Parse service arguments
parse_service_args([], Name, Ports, Bootstrap, ServiceType, create) :-
( Name \= ''
-> service_create(Name, Ports, Bootstrap, ServiceType)
; write(user_error, 'Error: --name required for service creation\n'),
halt(1)
).
parse_service_args([], _, _, _, _, Action) :-
( Action = list
-> service_list
; write(user_error, 'Error: Use --list, --info, --logs, --sleep, --wake, --destroy, or --name\n'),
halt(1)
).
parse_service_args(['--list'|_], _, _, _, _, _) :- service_list.
parse_service_args(['-l'|_], _, _, _, _, _) :- service_list.
parse_service_args(['--info', ServiceId|_], _, _, _, _, _) :- service_info(ServiceId).
parse_service_args(['--logs', ServiceId|_], _, _, _, _, _) :- service_logs(ServiceId).
parse_service_args(['--sleep', ServiceId|_], _, _, _, _, _) :- service_sleep(ServiceId).
parse_service_args(['--wake', ServiceId|_], _, _, _, _, _) :- service_wake(ServiceId).
parse_service_args(['--destroy', ServiceId|_], _, _, _, _, _) :- service_destroy(ServiceId).
parse_service_args(['--name', Name|Rest], _, Ports, Bootstrap, ServiceType, _) :-
parse_service_args(Rest, Name, Ports, Bootstrap, ServiceType, create).
parse_service_args(['--ports', PortsList|Rest], Name, _, Bootstrap, ServiceType, Action) :-
parse_service_args(Rest, Name, PortsList, Bootstrap, ServiceType, Action).
parse_service_args(['--bootstrap', BootstrapFile|Rest], Name, Ports, _, ServiceType, Action) :-
parse_service_args(Rest, Name, Ports, BootstrapFile, ServiceType, Action).
parse_service_args(['--type', Type|Rest], Name, Ports, Bootstrap, _, Action) :-
parse_service_args(Rest, Name, Ports, Bootstrap, Type, Action).
parse_service_args([_|Rest], Name, Ports, Bootstrap, ServiceType, Action) :-
parse_service_args(Rest, Name, Ports, Bootstrap, ServiceType, Action).
% Execute service action (not used, but kept for structure)
execute_service_action(_).
% Main program
main(Argv) :-

6
un.ps1
View file

@ -231,6 +231,11 @@ function Invoke-Service {
$payload["bootstrap"] = $Args[$bIdx + 1]
}
if ($Args -contains "--type") {
$tIdx = [array]::IndexOf($Args, "--type")
$payload["service_type"] = $Args[$tIdx + 1]
}
$body = $payload | ConvertTo-Json -Depth 5
$result = Invoke-Api -Endpoint "/services" -Method "POST" -Body $body
Write-Host "`e[32mService created`e[0m"
@ -261,6 +266,7 @@ Session options:
Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--type TYPE Service type (minecraft, mumble, teamspeak, source, tcp, udp)
--bootstrap CMD Bootstrap command
--list, -l List services
--info ID Get service info

3
un.py
View file

@ -330,6 +330,8 @@ def cmd_service(args):
payload["ports"] = [int(p) for p in args.ports.split(',')]
if args.domains:
payload["domains"] = args.domains.split(',')
if args.service_type:
payload["service_type"] = args.service_type
if args.bootstrap:
# Check if bootstrap is a file
if os.path.exists(args.bootstrap):
@ -396,6 +398,7 @@ Examples:
service_parser.add_argument("--name", help="Service name")
service_parser.add_argument("--ports", help="Comma-separated ports")
service_parser.add_argument("--domains", help="Comma-separated custom domains")
service_parser.add_argument("--type", dest="service_type", help="Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)")
service_parser.add_argument("--bootstrap", help="Bootstrap command/file")
service_parser.add_argument("-l", "--list", action="store_true", help="List services")
service_parser.add_argument("--info", metavar="ID", help="Get service details")

76
un.r
View file

@ -286,7 +286,49 @@ cmd_service <- function(args) {
return()
}
cat(sprintf("%sError: Use --list, --info, --logs, --sleep, --wake, or --destroy%s\n", RED, RESET), file = stderr())
if (!is.null(args$name)) {
payload <- list(name = args$name)
if (!is.null(args$ports)) {
ports_vec <- as.integer(strsplit(args$ports, ",")[[1]])
payload$ports <- ports_vec
}
if (!is.null(args$domains)) {
domains_vec <- strsplit(args$domains, ",")[[1]]
payload$domains <- domains_vec
}
if (!is.null(args$type)) {
payload$service_type <- args$type
}
if (!is.null(args$bootstrap)) {
if (file.exists(args$bootstrap)) {
payload$bootstrap <- paste(readLines(args$bootstrap, warn = FALSE), collapse = "\n")
} else {
payload$bootstrap <- args$bootstrap
}
}
if (!is.null(args$network)) {
payload$network <- args$network
}
if (!is.null(args$vcpu)) {
payload$vcpu <- args$vcpu
}
result <- api_request("/services", api_key, method = "POST", data = payload)
cat(sprintf("%sService created: %s%s\n", GREEN, if (!is.null(result$id)) result$id else "N/A", RESET))
cat(sprintf("Name: %s\n", if (!is.null(result$name)) result$name else "N/A"))
if (!is.null(result$url)) {
cat(sprintf("URL: %s\n", result$url))
}
return()
}
cat(sprintf("%sError: Use --name to create, or --list, --info, --logs, --sleep, --wake, --destroy%s\n", RED, RESET), file = stderr())
quit(status = 1)
}
@ -308,7 +350,13 @@ parse_args <- function() {
logs = NULL,
sleep = NULL,
wake = NULL,
destroy = NULL
destroy = NULL,
name = NULL,
ports = NULL,
domains = NULL,
type = NULL,
bootstrap = NULL,
vcpu = NULL
)
i <- 1
@ -371,6 +419,30 @@ parse_args <- function() {
i <- i + 1
result$destroy <- args[i]
i <- i + 1
} else if (arg == "--name") {
i <- i + 1
result$name <- args[i]
i <- i + 1
} else if (arg == "--ports") {
i <- i + 1
result$ports <- args[i]
i <- i + 1
} else if (arg == "--domains") {
i <- i + 1
result$domains <- args[i]
i <- i + 1
} else if (arg == "--type") {
i <- i + 1
result$type <- args[i]
i <- i + 1
} else if (arg == "--bootstrap") {
i <- i + 1
result$bootstrap <- args[i]
i <- i + 1
} else if (arg %in% c("-v", "--vcpu")) {
i <- i + 1
result$vcpu <- as.integer(args[i])
i <- i + 1
} else if (!startsWith(arg, "-")) {
result$source_file <- arg
i <- i + 1

View file

@ -320,6 +320,7 @@ sub cmd-service(@args) {
my $destroy-id = '';
my $name = '';
my $ports = '';
my $type = '';
my $bootstrap = '';
my $network = '';
my $vcpu = 0;
@ -359,6 +360,10 @@ sub cmd-service(@args) {
$i++;
$ports = @args[$i];
}
when '--type' {
$i++;
$type = @args[$i];
}
when '--bootstrap' {
$i++;
$bootstrap = @args[$i];
@ -430,6 +435,10 @@ sub cmd-service(@args) {
%payload<ports> = $ports.split(',')>>.Int;
}
if $type {
%payload<service_type> = $type;
}
if $bootstrap {
# Check if bootstrap is a file
if $bootstrap.IO.e && $bootstrap.IO.f {

6
un.rb
View file

@ -314,6 +314,7 @@ def cmd_service(options)
payload = { name: options[:name] }
payload[:ports] = options[:ports].split(',').map(&:to_i) if options[:ports]
payload[:domains] = options[:domains].split(',') if options[:domains]
payload[:service_type] = options[:type] if options[:type]
if options[:bootstrap]
payload[:bootstrap] = if File.exist?(options[:bootstrap])
File.read(options[:bootstrap])
@ -356,6 +357,7 @@ def main
name: nil,
ports: nil,
domains: nil,
type: nil,
bootstrap: nil,
info: nil,
logs: nil,
@ -421,6 +423,9 @@ def main
when '--domains'
i += 1
options[:domains] = ARGV[i]
when '--type'
i += 1
options[:type] = ARGV[i]
when '--bootstrap'
i += 1
options[:bootstrap] = ARGV[i]
@ -494,6 +499,7 @@ def main
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type (minecraft|mumble|teamspeak|source|tcp|udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

6
un.rs
View file

@ -338,6 +338,7 @@ fn cmd_service(
name: Option<&str>,
ports: Option<&str>,
domains: Option<&str>,
service_type: Option<&str>,
bootstrap: Option<&str>,
list: bool,
info: Option<&str>,
@ -420,6 +421,10 @@ fn cmd_service(
json.push(']');
}
if let Some(t) = service_type {
json.push_str(&format!(r#","service_type":"{}""#, t));
}
if let Some(b) = bootstrap {
let cmd = if Path::new(b).exists() {
fs::read_to_string(b).unwrap_or(b.to_string())
@ -559,6 +564,7 @@ fn main() {
args.iter().position(|x| x == "--name").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),
args.iter().position(|x| x == "--ports").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),
args.iter().position(|x| x == "--domains").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),
args.iter().position(|x| x == "--type").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),
args.iter().position(|x| x == "--bootstrap").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),
args.contains(&"--list".to_string()),
args.iter().position(|x| x == "--info").and_then(|p| args.get(p + 1)).map(|s| s.as_str()),

23
un.scm
View file

@ -172,7 +172,7 @@
(display response)
(newline))))))
(define (service-cmd action id name ports bootstrap)
(define (service-cmd action id name ports bootstrap type)
(let ((api-key (get-api-key)))
(cond
((equal? action "list")
@ -196,7 +196,8 @@
((and (equal? action "create") name)
(let* ((ports-json (if ports (format #f ",\"ports\":[~a]" ports) ""))
(bootstrap-json (if bootstrap (format #f ",\"bootstrap\":\"~a\"" (escape-json bootstrap)) ""))
(json (format #f "{\"name\":\"~a\"~a~a}" name ports-json bootstrap-json))
(type-json (if type (format #f ",\"service_type\":\"~a\"" type) ""))
(json (format #f "{\"name\":\"~a\"~a~a~a}" name ports-json bootstrap-json type-json))
(response (curl-post api-key "/services" json)))
(format #t "~aService created~a\n" green reset)
(display response)
@ -222,24 +223,26 @@
((equal? (car args) "service")
(cond
((and (> (length args) 1) (equal? (cadr args) "--list"))
(service-cmd "list" #f #f #f #f))
(service-cmd "list" #f #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--info"))
(service-cmd "info" (caddr args) #f #f #f))
(service-cmd "info" (caddr args) #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--logs"))
(service-cmd "logs" (caddr args) #f #f #f))
(service-cmd "logs" (caddr args) #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--sleep"))
(service-cmd "sleep" (caddr args) #f #f #f))
(service-cmd "sleep" (caddr args) #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--wake"))
(service-cmd "wake" (caddr args) #f #f #f))
(service-cmd "wake" (caddr args) #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--destroy"))
(service-cmd "destroy" (caddr args) #f #f #f))
(service-cmd "destroy" (caddr args) #f #f #f #f))
((and (> (length args) 2) (equal? (cadr args) "--name"))
(let ((name (caddr args))
(ports (if (and (> (length args) 4) (equal? (list-ref args 3) "--ports"))
(list-ref args 4) #f))
(bootstrap (if (and (> (length args) 6) (equal? (list-ref args 5) "--bootstrap"))
(list-ref args 6) #f)))
(service-cmd "create" #f name ports bootstrap)))
(list-ref args 6) #f))
(type (if (and (> (length args) 8) (equal? (list-ref args 7) "--type"))
(list-ref args 8) #f)))
(service-cmd "create" #f name ports bootstrap type)))
(else
(display "Error: Invalid service command\n" (current-error-port))
(exit 1))))

10
un.sh
View file

@ -395,6 +395,7 @@ cmd_service() {
local name=""
local ports=""
local domains=""
local service_type=""
local bootstrap=""
local list=false
local info=""
@ -423,6 +424,10 @@ cmd_service() {
domains="$2"
shift 2
;;
--type)
service_type="$2"
shift 2
;;
--bootstrap)
bootstrap="$2"
shift 2
@ -555,6 +560,10 @@ cmd_service() {
payload=$(echo "$payload" | jq --argjson d "$domains_json" '. + {domains: $d}')
fi
if [[ -n "$service_type" ]]; then
payload=$(echo "$payload" | jq --arg t "$service_type" '. + {service_type: $t}')
fi
if [[ -n "$bootstrap" ]]; then
if [[ -f "$bootstrap" ]]; then
local bootstrap_content=$(cat "$bootstrap")
@ -614,6 +623,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type for SRV records (minecraft, mumble, teamspeak, source, tcp, udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

9
un.tcl
View file

@ -361,6 +361,7 @@ proc cmd_service {args} {
set destroy_id ""
set name ""
set ports ""
set service_type ""
set bootstrap ""
set network ""
set vcpu 0
@ -400,6 +401,10 @@ proc cmd_service {args} {
incr i
set ports [lindex $args $i]
}
--type {
incr i
set service_type [lindex $args $i]
}
--bootstrap {
incr i
set bootstrap [lindex $args $i]
@ -479,6 +484,10 @@ proc cmd_service {args} {
lappend payload ports [::json::write array {*}$port_json]
}
if {$service_type ne ""} {
lappend payload service_type [::json::write string $service_type]
}
if {$bootstrap ne ""} {
# Check if bootstrap is a file
if {[file exists $bootstrap]} {

7
un.ts
View file

@ -98,6 +98,7 @@ interface Args {
name: string | null;
ports: string | null;
domains: string | null;
type: string | null;
bootstrap: string | null;
info: string | null;
logs: string | null;
@ -356,6 +357,7 @@ async function cmdService(args: Args): Promise<void> {
const payload: any = { name: args.name };
if (args.ports) payload.ports = args.ports.split(',').map(p => parseInt(p.trim()));
if (args.domains) payload.domains = args.domains.split(',');
if (args.type) payload.service_type = args.type;
if (args.bootstrap) {
if (fs.existsSync(args.bootstrap)) {
payload.bootstrap = fs.readFileSync(args.bootstrap, 'utf-8');
@ -398,6 +400,7 @@ function parseArgs(argv: string[]): Args {
name: null,
ports: null,
domains: null,
type: null,
bootstrap: null,
info: null,
logs: null,
@ -467,6 +470,9 @@ function parseArgs(argv: string[]): Args {
} else if (arg === '--domains' && i + 1 < argv.length) {
args.domains = argv[++i];
i++;
} else if (arg === '--type' && i + 1 < argv.length) {
args.type = argv[++i];
i++;
} else if (arg === '--bootstrap' && i + 1 < argv.length) {
args.bootstrap = argv[++i];
i++;
@ -545,6 +551,7 @@ Service options:
--name NAME Service name
--ports PORTS Comma-separated ports
--domains DOMAINS Custom domains
--type TYPE Service type (minecraft|mumble|teamspeak|source|tcp|udp)
--bootstrap CMD Bootstrap command/file
-l, --list List services
--info ID Get service details

12
un.v
View file

@ -176,7 +176,7 @@ fn cmd_session(list bool, kill string, shell string, network string, vcpu int, t
println(exec_curl(cmd))
}
fn cmd_service(name string, ports string, bootstrap string, list bool, info string, logs string, tail string, sleep string, wake string, destroy string, network string, vcpu int, api_key string) {
fn cmd_service(name string, ports string, service_type string, bootstrap string, list bool, info string, logs string, tail string, sleep string, wake string, destroy string, network string, vcpu int, api_key string) {
if list {
cmd := "curl -s -X GET '${api_base}/services' -H 'Authorization: Bearer ${api_key}'"
println(exec_curl(cmd))
@ -227,6 +227,9 @@ fn cmd_service(name string, ports string, bootstrap string, list bool, info stri
if ports != '' {
json += ',"ports":[${ports}]'
}
if service_type != '' {
json += ',"service_type":"${service_type}"'
}
if bootstrap != '' {
if os.exists(bootstrap) {
boot_code := os.read_file(bootstrap) or { bootstrap }
@ -310,6 +313,7 @@ fn main() {
if os.args[1] == 'service' {
mut name := ''
mut ports := ''
mut service_type := ''
mut bootstrap := ''
mut list := false
mut info := ''
@ -332,6 +336,10 @@ fn main() {
i++
ports = os.args[i]
}
'--type' {
i++
service_type = os.args[i]
}
'--bootstrap' {
i++
bootstrap = os.args[i]
@ -378,7 +386,7 @@ fn main() {
i++
}
cmd_service(name, ports, bootstrap, list, info, logs, tail, sleep, wake, destroy, network,
cmd_service(name, ports, service_type, bootstrap, list, info, logs, tail, sleep, wake, destroy, network,
vcpu, api_key)
return
}

7
un.zig
View file

@ -117,6 +117,7 @@ pub fn main() !u8 {
var list = false;
var name: ?[]const u8 = null;
var ports: ?[]const u8 = null;
var service_type: ?[]const u8 = null;
var info: ?[]const u8 = null;
var i: usize = 2;
while (i < args.len) : (i += 1) {
@ -128,6 +129,9 @@ pub fn main() !u8 {
} else if (mem.eql(u8, args[i], "--ports") and i + 1 < args.len) {
i += 1;
ports = args[i];
} else if (mem.eql(u8, args[i], "--type") and i + 1 < args.len) {
i += 1;
service_type = args[i];
} else if (mem.eql(u8, args[i], "--info") and i + 1 < args.len) {
i += 1;
info = args[i];
@ -152,6 +156,9 @@ pub fn main() !u8 {
if (ports) |p| {
try writer.print(",\"ports\":[{s}]", .{p});
}
if (service_type) |t| {
try writer.print(",\"service_type\":\"{s}\"", .{t});
}
try writer.writeAll("}");
const json_str = json_stream.getWritten();

View file

@ -338,6 +338,7 @@ async function cmdService(args: string[]) {
let destroyId = "";
let name = "";
let ports = "";
let serviceType = "";
let bootstrap = "";
let network = "";
let vcpu = 0;
@ -384,6 +385,11 @@ async function cmdService(args: string[]) {
ports = args[++i];
}
break;
case "--type":
if (i + 1 < args.length) {
serviceType = args[++i];
}
break;
case "--bootstrap":
if (i + 1 < args.length) {
bootstrap = args[++i];
@ -460,6 +466,10 @@ async function cmdService(args: string[]) {
payload.ports = ports.split(",").map((p) => parseInt(p));
}
if (serviceType) {
payload.service_type = serviceType;
}
if (bootstrap) {
// Check if bootstrap is a file
try {

View file

@ -312,7 +312,7 @@ void cmd_session(int list, const char *kill, const char *shell, const char *netw
printf("\n%sSession created%s\n", GREEN, RESET);
}
void cmd_service(const char *name, const char *ports, const char *domains, const char *bootstrap, int list, const char *info, const char *logs, const char *tail, const char *sleep_svc, const char *wake, const char *destroy, const char *network, int vcpu, const char *api_key) {
void cmd_service(const char *name, const char *ports, const char *domains, const char *service_type, const char *bootstrap, int list, const char *info, const char *logs, const char *tail, const char *sleep_svc, const char *wake, const char *destroy, const char *network, int vcpu, const char *api_key) {
char cmd[8192];
if (list) {
@ -384,6 +384,11 @@ void cmd_service(const char *name, const char *ports, const char *domains, const
snprintf(temp, sizeof(temp), ",\"ports\":[%s]", ports);
strcat(json, temp);
}
if (service_type) {
char temp[128];
snprintf(temp, sizeof(temp), ",\"service_type\":\"%s\"", service_type);
strcat(json, temp);
}
if (bootstrap) {
// Check if file
struct stat st;
@ -465,6 +470,7 @@ int main(int argc, char *argv[]) {
const char *name = NULL;
const char *ports = NULL;
const char *domains = NULL;
const char *service_type = NULL;
const char *bootstrap = NULL;
int list = 0;
const char *info = NULL;
@ -480,6 +486,7 @@ int main(int argc, char *argv[]) {
if (strcmp(argv[i], "--name") == 0 && i + 1 < argc) name = argv[++i];
else if (strcmp(argv[i], "--ports") == 0 && i + 1 < argc) ports = argv[++i];
else if (strcmp(argv[i], "--domains") == 0 && i + 1 < argc) domains = argv[++i];
else if (strcmp(argv[i], "--type") == 0 && i + 1 < argc) service_type = argv[++i];
else if (strcmp(argv[i], "--bootstrap") == 0 && i + 1 < argc) bootstrap = argv[++i];
else if (strcmp(argv[i], "--list") == 0) list = 1;
else if (strcmp(argv[i], "--info") == 0 && i + 1 < argc) info = argv[++i];
@ -493,7 +500,7 @@ int main(int argc, char *argv[]) {
else if (strcmp(argv[i], "-k") == 0 && i + 1 < argc) api_key = argv[++i];
}
cmd_service(name, ports, domains, bootstrap, list, info, logs, tail, sleep_svc, wake, destroy, network, vcpu, api_key);
cmd_service(name, ports, domains, service_type, bootstrap, list, info, logs, tail, sleep_svc, wake, destroy, network, vcpu, api_key);
return 0;
}