Updated awk, bash, clojure, cobol, cpp, crystal, csharp, dart, dotnet, d, elixir, erlang, fortran, fsharp, groovy, haskell, julia, kotlin, lisp, lua, nim, objective-c, ocaml, perl, php (sync+async), powershell, prolog, raku, r, scheme, swift, tcl, v, and zig SDKs: - Add set_unfreeze_on_demand function (PATCH /services/:id) - Add --unfreeze-on-demand flag for service creation - Add --set-unfreeze-on-demand command for existing services
929 lines
56 KiB
Clojure
929 lines
56 KiB
Clojure
;; PUBLIC DOMAIN - NO LICENSE, NO WARRANTY
|
|
;;
|
|
;; This is free public domain software for the public good of a permacomputer hosted
|
|
;; at permacomputer.com - an always-on computer by the people, for the people. One
|
|
;; which is durable, easy to repair, and distributed like tap water for machine
|
|
;; learning intelligence.
|
|
;;
|
|
;; The permacomputer is community-owned infrastructure optimized around four values:
|
|
;;
|
|
;; TRUTH - First principles, math & science, open source code freely distributed
|
|
;; FREEDOM - Voluntary partnerships, freedom from tyranny & corporate control
|
|
;; HARMONY - Minimal waste, self-renewing systems with diverse thriving connections
|
|
;; LOVE - Be yourself without hurting others, cooperation through natural law
|
|
;;
|
|
;; This software contributes to that vision by enabling code execution across 42+
|
|
;; programming languages through a unified interface, accessible to all. Code is
|
|
;; seeds to sprout on any abandoned technology.
|
|
;;
|
|
;; Learn more: https://www.permacomputer.com
|
|
;;
|
|
;; Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
;; software, either in source code form or as a compiled binary, for any purpose,
|
|
;; commercial or non-commercial, and by any means.
|
|
;;
|
|
;; NO WARRANTY. THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
|
|
;;
|
|
;; That said, our permacomputer's digital membrane stratum continuously runs unit,
|
|
;; integration, and functional tests on all of it's own software - with our
|
|
;; permacomputer monitoring itself, repairing itself, with minimal human in the
|
|
;; loop guidance. Our agents do their best.
|
|
;;
|
|
;; Copyright 2025 TimeHexOn & foxhop & russell@unturf
|
|
;; https://www.timehexon.com
|
|
;; https://www.foxhop.net
|
|
;; https://www.unturf.com/software
|
|
|
|
|
|
#!/usr/bin/env bb
|
|
|
|
;; Clojure UN CLI - Unsandbox CLI Client
|
|
;;
|
|
;; Full-featured CLI matching un.py capabilities:
|
|
;; - Execute code with env vars, input files, artifacts
|
|
;; - Interactive sessions with shell/REPL support
|
|
;; - Persistent services with domains and ports
|
|
;;
|
|
;; Usage:
|
|
;; chmod +x un.clj
|
|
;; export UNSANDBOX_API_KEY="your_key_here"
|
|
;; ./un.clj [options] <source_file>
|
|
;; ./un.clj session [options]
|
|
;; ./un.clj service [options]
|
|
;;
|
|
;; Uses curl for HTTP (no external dependencies)
|
|
|
|
(require '[clojure.java.io :as io]
|
|
'[clojure.string :as str]
|
|
'[clojure.java.shell :refer [sh]])
|
|
|
|
(def blue "\u001b[34m")
|
|
(def red "\u001b[31m")
|
|
(def green "\u001b[32m")
|
|
(def yellow "\u001b[33m")
|
|
(def reset "\u001b[0m")
|
|
|
|
(def portal-base "https://unsandbox.com")
|
|
|
|
(def languages-cache-ttl 3600) ;; 1 hour in seconds
|
|
|
|
(def ext-map
|
|
{".hs" "haskell" ".ml" "ocaml" ".clj" "clojure" ".scm" "scheme"
|
|
".lisp" "commonlisp" ".erl" "erlang" ".ex" "elixir" ".exs" "elixir"
|
|
".py" "python" ".js" "javascript" ".ts" "typescript" ".rb" "ruby"
|
|
".go" "go" ".rs" "rust" ".c" "c" ".cpp" "cpp" ".cc" "cpp"
|
|
".cxx" "cpp" ".java" "java" ".kt" "kotlin" ".cs" "csharp"
|
|
".fs" "fsharp" ".jl" "julia" ".r" "r" ".cr" "crystal"
|
|
".d" "d" ".nim" "nim" ".zig" "zig" ".v" "v" ".dart" "dart"
|
|
".groovy" "groovy" ".scala" "scala" ".sh" "bash" ".pl" "perl"
|
|
".lua" "lua" ".php" "php"})
|
|
|
|
(defn get-extension [filename]
|
|
(let [dot-pos (str/last-index-of filename ".")]
|
|
(if dot-pos (subs filename dot-pos) "")))
|
|
|
|
(defn escape-json [s]
|
|
(-> s
|
|
(str/replace "\\" "\\\\")
|
|
(str/replace "\"" "\\\"")
|
|
(str/replace "\n" "\\n")
|
|
(str/replace "\r" "\\r")
|
|
(str/replace "\t" "\\t")))
|
|
|
|
(defn unescape-json [s]
|
|
(-> s
|
|
(str/replace "\\n" "\n")
|
|
(str/replace "\\t" "\t")
|
|
(str/replace "\\\"" "\"")
|
|
(str/replace "\\\\" "\\")))
|
|
|
|
(defn read-and-base64 [filepath]
|
|
(let [content (slurp filepath)
|
|
bytes (.getBytes content "UTF-8")]
|
|
(.encodeToString (java.util.Base64/getEncoder) bytes)))
|
|
|
|
(defn build-input-files-json [files]
|
|
(if (empty? files)
|
|
""
|
|
(let [file-jsons (map (fn [f]
|
|
(let [basename (-> (io/file f) .getName)
|
|
b64 (read-and-base64 f)]
|
|
(str "{\"filename\":\"" (escape-json basename) "\",\"content\":\"" b64 "\"}")))
|
|
files)]
|
|
(str ",\"input_files\":[" (str/join "," file-jsons) "]"))))
|
|
|
|
(defn extract-field [field json-str]
|
|
(let [pattern-str (re-pattern (str "\"" field "\":\"([^\"]*)\""))
|
|
pattern-num (re-pattern (str "\"" field "\":(\\d+)"))]
|
|
(or (second (re-find pattern-str json-str))
|
|
(second (re-find pattern-num json-str)))))
|
|
|
|
(defn get-api-keys []
|
|
(let [public-key (System/getenv "UNSANDBOX_PUBLIC_KEY")
|
|
secret-key (System/getenv "UNSANDBOX_SECRET_KEY")
|
|
api-key (System/getenv "UNSANDBOX_API_KEY")]
|
|
(cond
|
|
(and public-key secret-key) [public-key secret-key]
|
|
api-key [api-key nil]
|
|
:else (do
|
|
(binding [*out* *err*]
|
|
(println "Error: UNSANDBOX_PUBLIC_KEY and UNSANDBOX_SECRET_KEY not set (or UNSANDBOX_API_KEY for backwards compat)"))
|
|
(System/exit 1)))))
|
|
|
|
(defn get-api-key []
|
|
(first (get-api-keys)))
|
|
|
|
(defn hmac-sha256 [secret message]
|
|
(let [mac (javax.crypto.Mac/getInstance "HmacSHA256")
|
|
secret-key (javax.crypto.spec.SecretKeySpec. (.getBytes secret "UTF-8") "HmacSHA256")]
|
|
(.init mac secret-key)
|
|
(let [bytes (.doFinal mac (.getBytes message "UTF-8"))]
|
|
(apply str (map #(format "%02x" %) bytes)))))
|
|
|
|
(defn make-signature [secret-key timestamp method path body]
|
|
(let [message (str timestamp ":" method ":" path ":" body)]
|
|
(hmac-sha256 secret-key message)))
|
|
|
|
(defn check-clock-drift-error [response]
|
|
(let [has-timestamp (or (str/includes? response "timestamp")
|
|
(str/includes? response "\"timestamp\""))
|
|
has-401 (str/includes? response "401")
|
|
has-expired (str/includes? response "expired")
|
|
has-invalid (str/includes? response "invalid")]
|
|
(when (and has-timestamp (or has-401 has-expired has-invalid))
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Request timestamp expired (must be within 5 minutes of server time)" reset))
|
|
(println (str yellow "Your computer's clock may have drifted." reset))
|
|
(println "Check your system time and sync with NTP if needed:")
|
|
(println " Linux: sudo ntpdate -s time.nist.gov")
|
|
(println " macOS: sudo sntp -sS time.apple.com")
|
|
(println " Windows: w32tm /resync"))
|
|
(System/exit 1))))
|
|
|
|
(defn build-auth-headers [public-key secret-key method path body]
|
|
(if secret-key
|
|
(let [timestamp (str (quot (System/currentTimeMillis) 1000))
|
|
signature (make-signature secret-key timestamp method path body)]
|
|
["-H" (str "Authorization: Bearer " public-key)
|
|
"-H" (str "X-Timestamp: " timestamp)
|
|
"-H" (str "X-Signature: " signature)])
|
|
["-H" (str "Authorization: Bearer " public-key)]))
|
|
|
|
(defn curl-post [api-key endpoint json-data]
|
|
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".json")
|
|
[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "POST" endpoint json-data)]
|
|
(spit tmp-file json-data)
|
|
(let [args (concat ["curl" "-s" "-X" "POST"
|
|
(str "https://api.unsandbox.com" endpoint)
|
|
"-H" "Content-Type: application/json"]
|
|
auth-headers
|
|
["-d" (str "@" tmp-file)])
|
|
{:keys [out]} (apply sh args)]
|
|
(io/delete-file tmp-file true)
|
|
(check-clock-drift-error out)
|
|
out)))
|
|
|
|
(defn curl-get [api-key endpoint]
|
|
(let [[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "GET" endpoint "")
|
|
args (concat ["curl" "-s"
|
|
(str "https://api.unsandbox.com" endpoint)]
|
|
auth-headers)
|
|
result (:out (apply sh args))]
|
|
(check-clock-drift-error result)
|
|
result))
|
|
|
|
(defn curl-delete [api-key endpoint]
|
|
(let [[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "DELETE" endpoint "")
|
|
args (concat ["curl" "-s" "-X" "DELETE"
|
|
(str "https://api.unsandbox.com" endpoint)]
|
|
auth-headers)
|
|
result (:out (apply sh args))]
|
|
(check-clock-drift-error result)
|
|
result))
|
|
|
|
(defn curl-put-text [endpoint body]
|
|
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".txt")
|
|
[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "PUT" endpoint body)]
|
|
(spit tmp-file body)
|
|
(let [args (concat ["curl" "-s" "-o" "/dev/null" "-w" "%{http_code}" "-X" "PUT"
|
|
(str "https://api.unsandbox.com" endpoint)
|
|
"-H" "Content-Type: text/plain"]
|
|
auth-headers
|
|
["-d" (str "@" tmp-file)])
|
|
{:keys [out]} (apply sh args)]
|
|
(io/delete-file tmp-file true)
|
|
(let [status (Integer/parseInt (str/trim out))]
|
|
(and (>= status 200) (< status 300))))))
|
|
|
|
(defn curl-patch [api-key endpoint json-data]
|
|
(let [tmp-file (str "/tmp/un_clj_" (rand-int 999999) ".json")
|
|
[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "PATCH" endpoint json-data)]
|
|
(spit tmp-file json-data)
|
|
(let [args (concat ["curl" "-s" "-X" "PATCH"
|
|
(str "https://api.unsandbox.com" endpoint)
|
|
"-H" "Content-Type: application/json"]
|
|
auth-headers
|
|
["-d" (str "@" tmp-file)])
|
|
{:keys [out]} (apply sh args)]
|
|
(io/delete-file tmp-file true)
|
|
(check-clock-drift-error out)
|
|
out)))
|
|
|
|
(defn set-unfreeze-on-demand [service-id enabled]
|
|
(let [api-key (get-api-key)
|
|
json (str "{\"unfreeze_on_demand\":" (if enabled "true" "false") "}")]
|
|
(curl-patch api-key (str "/services/" service-id) json)
|
|
(println (str green "Service unfreeze_on_demand set to " (if enabled "true" "false") reset))))
|
|
|
|
(def max-env-content-size 65536)
|
|
|
|
(defn read-env-file [path]
|
|
(if (.exists (io/file path))
|
|
(slurp path)
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Env file not found: " path reset)))
|
|
(System/exit 1))))
|
|
|
|
(defn build-env-content [envs env-file]
|
|
(let [file-lines (if env-file
|
|
(->> (str/split (read-env-file env-file) #"\n")
|
|
(map str/trim)
|
|
(filter #(and (> (count %) 0) (not (.startsWith % "#")))))
|
|
[])]
|
|
(str/join "\n" (concat envs file-lines))))
|
|
|
|
(defn service-env-status [service-id]
|
|
(let [api-key (get-api-key)]
|
|
(curl-get api-key (str "/services/" service-id "/env"))))
|
|
|
|
(defn service-env-set [service-id env-content]
|
|
(if (> (count env-content) max-env-content-size)
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Env content exceeds maximum size of 64KB" reset)))
|
|
false)
|
|
(curl-put-text (str "/services/" service-id "/env") env-content)))
|
|
|
|
(defn service-env-export [service-id]
|
|
(let [api-key (get-api-key)]
|
|
(curl-post api-key (str "/services/" service-id "/env/export") "{}")))
|
|
|
|
(defn service-env-delete [service-id]
|
|
(let [api-key (get-api-key)]
|
|
(try
|
|
(curl-delete api-key (str "/services/" service-id "/env"))
|
|
true
|
|
(catch Exception _ false))))
|
|
|
|
(defn service-env-command [action target envs env-file]
|
|
(case action
|
|
"status" (if target
|
|
(let [response (service-env-status target)
|
|
has-vault (= (extract-field "has_vault" response) "true")]
|
|
(if has-vault
|
|
(do
|
|
(println (str green "Vault: configured" reset))
|
|
(when-let [env-count (extract-field "env_count" response)]
|
|
(println (str "Variables: " env-count)))
|
|
(when-let [updated-at (extract-field "updated_at" response)]
|
|
(println (str "Updated: " updated-at))))
|
|
(println (str yellow "Vault: not configured" reset))))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: service env status requires service ID" reset)))
|
|
(System/exit 1)))
|
|
"set" (if target
|
|
(if (and (empty? envs) (nil? env-file))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: service env set requires -e or --env-file" reset)))
|
|
(System/exit 1))
|
|
(let [env-content (build-env-content envs env-file)]
|
|
(if (service-env-set target env-content)
|
|
(println (str green "Vault updated for service " target reset))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Failed to update vault" reset)))
|
|
(System/exit 1)))))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: service env set requires service ID" reset)))
|
|
(System/exit 1)))
|
|
"export" (if target
|
|
(let [response (service-env-export target)
|
|
content (extract-field "content" response)]
|
|
(when content (print (unescape-json content))))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: service env export requires service ID" reset)))
|
|
(System/exit 1)))
|
|
"delete" (if target
|
|
(if (service-env-delete target)
|
|
(println (str green "Vault deleted for service " target reset))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Failed to delete vault" reset)))
|
|
(System/exit 1)))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: service env delete requires service ID" reset)))
|
|
(System/exit 1)))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Unknown env action: " action reset))
|
|
(println "Usage: un.clj service env <status|set|export|delete> <service_id>"))
|
|
(System/exit 1))))
|
|
|
|
(defn curl-portal-post [api-key endpoint json-data]
|
|
(let [tmp-file (str "/tmp/un_clj_portal_" (rand-int 999999) ".json")
|
|
[public-key secret-key] (get-api-keys)
|
|
auth-headers (build-auth-headers public-key secret-key "POST" endpoint json-data)]
|
|
(spit tmp-file json-data)
|
|
(let [args (concat ["curl" "-s" "-X" "POST"
|
|
(str portal-base endpoint)
|
|
"-H" "Content-Type: application/json"]
|
|
auth-headers
|
|
["-d" (str "@" tmp-file)])
|
|
{:keys [out]} (apply sh args)]
|
|
(io/delete-file tmp-file true)
|
|
(check-clock-drift-error out)
|
|
out)))
|
|
|
|
(defn execute-command [file env-vars artifacts out-dir network vcpu]
|
|
(let [api-key (get-api-key)
|
|
ext (get-extension file)
|
|
language (get ext-map ext)]
|
|
(when-not language
|
|
(binding [*out* *err*]
|
|
(println (str "Error: Unknown extension: " ext)))
|
|
(System/exit 1))
|
|
(let [code (slurp file)
|
|
env-json (if (empty? env-vars) ""
|
|
(str ",\"env\":{"
|
|
(str/join "," (map (fn [[k v]]
|
|
(str "\"" k "\":\"" (escape-json v) "\""))
|
|
env-vars))
|
|
"}"))
|
|
artifacts-json (if artifacts ",\"return_artifacts\":true" "")
|
|
network-json (if network (str ",\"network\":\"" network "\"") "")
|
|
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
|
|
json (str "{\"language\":\"" language "\",\"code\":\"" (escape-json code) "\""
|
|
env-json artifacts-json network-json vcpu-json "}")]
|
|
(let [response (curl-post api-key "/execute" json)
|
|
stdout-val (extract-field "stdout" response)
|
|
stderr-val (extract-field "stderr" response)
|
|
exit-code (or (some-> (extract-field "exit_code" response) Integer/parseInt) 0)]
|
|
(when stdout-val
|
|
(print (str blue (unescape-json stdout-val) reset))
|
|
(flush))
|
|
(when stderr-val
|
|
(binding [*out* *err*]
|
|
(print (str red (unescape-json stderr-val) reset))
|
|
(flush)))
|
|
(System/exit exit-code)))))
|
|
|
|
(defn session-command [action sid shell network vcpu input-files]
|
|
(let [api-key (get-api-key)]
|
|
(case action
|
|
:list (println (curl-get api-key "/sessions"))
|
|
:kill (do
|
|
(curl-delete api-key (str "/sessions/" sid))
|
|
(println (str green "Session terminated: " sid reset)))
|
|
:create (let [sh (or shell "bash")
|
|
network-json (if network (str ",\"network\":\"" network "\"") "")
|
|
vcpu-json (if vcpu (str ",\"vcpu\":" vcpu) "")
|
|
input-files-json (build-input-files-json input-files)
|
|
json (str "{\"shell\":\"" sh "\"" network-json vcpu-json input-files-json "}")]
|
|
(println (str yellow "Session created (WebSocket required)" reset))
|
|
(println (curl-post api-key "/sessions" json))))))
|
|
|
|
(defn service-command [action sid name ports bootstrap bootstrap-file service-type network vcpu input-files envs env-file unfreeze-on-demand]
|
|
(let [api-key (get-api-key)]
|
|
(case action
|
|
:env (service-env-command sid name envs env-file)
|
|
:list (println (curl-get api-key "/services"))
|
|
:info (println (curl-get api-key (str "/services/" sid)))
|
|
:logs (println (curl-get api-key (str "/services/" sid "/logs")))
|
|
:sleep (do
|
|
(curl-post api-key (str "/services/" sid "/freeze") "{}")
|
|
(println (str green "Service frozen: " sid reset)))
|
|
:wake (do
|
|
(curl-post api-key (str "/services/" sid "/unfreeze") "{}")
|
|
(println (str green "Service unfreezing: " sid reset)))
|
|
:destroy (do
|
|
(curl-delete api-key (str "/services/" sid))
|
|
(println (str green "Service destroyed: " sid reset)))
|
|
:resize (when sid
|
|
(if (or (nil? vcpu) (< vcpu 1) (> vcpu 8))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: --resize requires -v N (1-8)" reset)))
|
|
(System/exit 1))
|
|
(let [json (str "{\"vcpu\":" vcpu "}")
|
|
_ (curl-patch api-key (str "/services/" sid) json)
|
|
ram (* vcpu 2)]
|
|
(println (str green "Service resized to " vcpu " vCPU, " ram " GB RAM" reset)))))
|
|
:set-unfreeze-on-demand (when sid
|
|
(set-unfreeze-on-demand sid unfreeze-on-demand))
|
|
:execute (when (and sid bootstrap)
|
|
(let [json (str "{\"command\":\"" (escape-json bootstrap) "\"}")
|
|
response (curl-post api-key (str "/services/" sid "/execute") json)
|
|
stdout-val (extract-field "stdout" response)]
|
|
(when stdout-val
|
|
(print (str blue (unescape-json stdout-val) reset))
|
|
(flush))))
|
|
:dump-bootstrap (when sid
|
|
(binding [*out* *err*]
|
|
(println (str "Fetching bootstrap script from " sid "...")))
|
|
(let [json "{\"command\":\"cat /tmp/bootstrap.sh\"}"
|
|
response (curl-post api-key (str "/services/" sid "/execute") json)
|
|
stdout-val (extract-field "stdout" response)]
|
|
(if stdout-val
|
|
(let [script (unescape-json stdout-val)]
|
|
(if service-type
|
|
(do
|
|
(spit service-type script)
|
|
(sh "chmod" "755" service-type)
|
|
(println (str "Bootstrap saved to " service-type)))
|
|
(print script)))
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: Failed to fetch bootstrap (service not running or no bootstrap file)" reset)))
|
|
(System/exit 1)))))
|
|
:create (when name
|
|
(let [ports-json (if ports (str ",\"ports\":[" ports "]") "")
|
|
bootstrap-json (if bootstrap (str ",\"bootstrap\":\"" (escape-json bootstrap) "\"") "")
|
|
bootstrap-content-json (if bootstrap-file
|
|
(str ",\"bootstrap_content\":\"" (escape-json (slurp bootstrap-file)) "\"")
|
|
"")
|
|
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) "")
|
|
unfreeze-on-demand-json (if (some? unfreeze-on-demand) (str ",\"unfreeze_on_demand\":" (if unfreeze-on-demand "true" "false")) "")
|
|
input-files-json (build-input-files-json input-files)
|
|
json (str "{\"name\":\"" name "\"" ports-json bootstrap-json bootstrap-content-json service-type-json network-json vcpu-json unfreeze-on-demand-json input-files-json "}")
|
|
response (curl-post api-key "/services" json)
|
|
service-id (extract-field "id" response)]
|
|
(println (str green "Service created" reset))
|
|
(println response)
|
|
;; Auto-set vault if env vars were provided
|
|
(when (and service-id (or (seq envs) env-file))
|
|
(let [env-content (build-env-content envs env-file)]
|
|
(when (> (count env-content) 0)
|
|
(if (service-env-set service-id env-content)
|
|
(println (str green "Vault configured with environment variables" reset))
|
|
(println (str yellow "Warning: Failed to set vault" reset))))))))))))
|
|
|
|
(defn validate-key [api-key extend?]
|
|
(let [response (curl-portal-post api-key "/keys/validate" "{}")
|
|
status (extract-field "status" response)
|
|
public-key (extract-field "public_key" response)
|
|
tier (extract-field "tier" response)
|
|
valid-through (extract-field "valid_through_datetime" response)
|
|
valid-for (extract-field "valid_for_human" response)
|
|
rate-limit (extract-field "rate_per_minute" response)
|
|
burst (extract-field "burst" response)
|
|
concurrency (extract-field "concurrency" response)
|
|
expired-at (extract-field "expired_at_datetime" response)]
|
|
(cond
|
|
(= status "valid")
|
|
(do
|
|
(println (str green "Valid" reset "\n"))
|
|
(when public-key (println (str "Public Key: " public-key)))
|
|
(when tier (println (str "Tier: " tier)))
|
|
(println "Status: valid")
|
|
(when valid-through (println (str "Expires: " valid-through)))
|
|
(when valid-for (println (str "Time Remaining: " valid-for)))
|
|
(when rate-limit (println (str "Rate Limit: " rate-limit "/min")))
|
|
(when burst (println (str "Burst: " burst)))
|
|
(when concurrency (println (str "Concurrency: " concurrency)))
|
|
(when extend?
|
|
(let [url (str portal-base "/keys/extend?pk=" public-key)]
|
|
(println (str blue "Opening browser to extend key..." reset))
|
|
(sh "xdg-open" url))))
|
|
|
|
(= status "expired")
|
|
(do
|
|
(println (str red "Expired" reset "\n"))
|
|
(when public-key (println (str "Public Key: " public-key)))
|
|
(when tier (println (str "Tier: " tier)))
|
|
(when expired-at (println (str "Expired: " expired-at)))
|
|
(println (str "\n" yellow "To renew:" reset " Visit " portal-base "/keys/extend"))
|
|
(when extend?
|
|
(let [url (str portal-base "/keys/extend?pk=" public-key)]
|
|
(println (str blue "Opening browser..." reset))
|
|
(sh "xdg-open" url))))
|
|
|
|
:else
|
|
(do
|
|
(println (str red "Invalid" reset))
|
|
(println response)))))
|
|
|
|
(defn key-command [extend?]
|
|
(let [api-key (get-api-key)]
|
|
(validate-key api-key extend?)))
|
|
|
|
(defn get-languages-cache-path []
|
|
(let [home (System/getenv "HOME")]
|
|
(str home "/.unsandbox/languages.json")))
|
|
|
|
(defn load-languages-cache []
|
|
(let [cache-path (get-languages-cache-path)]
|
|
(when (.exists (io/file cache-path))
|
|
(try
|
|
(let [content (slurp cache-path)
|
|
timestamp (extract-field "timestamp" content)]
|
|
(when timestamp
|
|
(let [cache-time (Long/parseLong timestamp)
|
|
now (quot (System/currentTimeMillis) 1000)]
|
|
(when (< (- now cache-time) languages-cache-ttl)
|
|
content))))
|
|
(catch Exception _ nil)))))
|
|
|
|
(defn save-languages-cache [languages-json]
|
|
(let [cache-path (get-languages-cache-path)
|
|
cache-dir (.getParent (io/file cache-path))]
|
|
(.mkdirs (io/file cache-dir))
|
|
(let [timestamp (quot (System/currentTimeMillis) 1000)
|
|
cache-content (str "{\"languages\":" languages-json ",\"timestamp\":" timestamp "}")]
|
|
(spit cache-path cache-content))))
|
|
|
|
(defn languages-command [json-output?]
|
|
(let [api-key (get-api-key)
|
|
;; Try cache first
|
|
cached (load-languages-cache)
|
|
response (if cached
|
|
cached
|
|
(let [resp (curl-get api-key "/languages")]
|
|
;; Extract and cache the languages array
|
|
(when-let [match (re-find #"\"languages\":\s*\[([^\]]*)\]" resp)]
|
|
(save-languages-cache (str "[" (second match) "]")))
|
|
resp))]
|
|
(if json-output?
|
|
;; Extract language names and output as JSON array
|
|
(let [langs (re-seq #"\"name\":\"([^\"]+)\"" response)
|
|
names (map second langs)]
|
|
(println (str "[" (str/join "," (map #(str "\"" % "\"") names)) "]")))
|
|
;; Output one language per line
|
|
(let [langs (re-seq #"\"name\":\"([^\"]+)\"" response)
|
|
names (map second langs)]
|
|
(doseq [name names]
|
|
(println name))))))
|
|
|
|
;; Image command functions
|
|
(defn image-list []
|
|
(let [api-key (get-api-key)]
|
|
(println (curl-get api-key "/images"))))
|
|
|
|
(defn image-info [id]
|
|
(let [api-key (get-api-key)]
|
|
(println (curl-get api-key (str "/images/" id)))))
|
|
|
|
(defn image-delete [id]
|
|
(let [api-key (get-api-key)]
|
|
(curl-delete api-key (str "/images/" id))
|
|
(println (str green "Image deleted: " id reset))))
|
|
|
|
(defn image-lock [id]
|
|
(let [api-key (get-api-key)]
|
|
(curl-post api-key (str "/images/" id "/lock") "{}")
|
|
(println (str green "Image locked: " id reset))))
|
|
|
|
(defn image-unlock [id]
|
|
(let [api-key (get-api-key)]
|
|
(curl-post api-key (str "/images/" id "/unlock") "{}")
|
|
(println (str green "Image unlocked: " id reset))))
|
|
|
|
(defn image-publish [source-id source-type name]
|
|
(let [api-key (get-api-key)
|
|
json (str "{\"source_type\":\"" source-type "\",\"source_id\":\"" source-id "\""
|
|
(if name (str ",\"name\":\"" (escape-json name) "\"") "")
|
|
"}")]
|
|
(println (str green "Image published" reset))
|
|
(println (curl-post api-key "/images/publish" json))))
|
|
|
|
(defn image-visibility [id visibility]
|
|
(let [api-key (get-api-key)
|
|
json (str "{\"visibility\":\"" visibility "\"}")]
|
|
(curl-post api-key (str "/images/" id "/visibility") json)
|
|
(println (str green "Image visibility set to: " visibility reset))))
|
|
|
|
(defn image-spawn [id name ports]
|
|
(let [api-key (get-api-key)
|
|
json (str "{"
|
|
(if name (str "\"name\":\"" (escape-json name) "\"") "")
|
|
(if (and name ports) "," "")
|
|
(if ports (str "\"ports\":[" ports "]") "")
|
|
"}")]
|
|
(println (str green "Service spawned from image" reset))
|
|
(println (curl-post api-key (str "/images/" id "/spawn") json))))
|
|
|
|
(defn image-clone [id name]
|
|
(let [api-key (get-api-key)
|
|
json (str "{" (if name (str "\"name\":\"" (escape-json name) "\"") "") "}")]
|
|
(println (str green "Image cloned" reset))
|
|
(println (curl-post api-key (str "/images/" id "/clone") json))))
|
|
|
|
(defn image-command [action id source-type visibility name ports]
|
|
(case action
|
|
:list (image-list)
|
|
:info (image-info id)
|
|
:delete (image-delete id)
|
|
:lock (image-lock id)
|
|
:unlock (image-unlock id)
|
|
:publish (if source-type
|
|
(image-publish id source-type name)
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: --publish requires --source-type (service or snapshot)" reset)))
|
|
(System/exit 1)))
|
|
:visibility (if visibility
|
|
(image-visibility id visibility)
|
|
(do
|
|
(binding [*out* *err*]
|
|
(println (str red "Error: --visibility requires visibility mode (private, unlisted, public)" reset)))
|
|
(System/exit 1)))
|
|
:spawn (image-spawn id name ports)
|
|
:clone (image-clone id name)))
|
|
|
|
(defn parse-args [args]
|
|
(loop [args args
|
|
file nil
|
|
env-vars []
|
|
artifacts false
|
|
out-dir nil
|
|
network nil
|
|
vcpu nil
|
|
session-action nil
|
|
session-id nil
|
|
session-shell nil
|
|
session-input-files []
|
|
service-action nil
|
|
service-id nil
|
|
service-name nil
|
|
service-ports nil
|
|
service-bootstrap nil
|
|
service-bootstrap-file nil
|
|
service-type nil
|
|
service-input-files []
|
|
service-envs []
|
|
service-env-file nil
|
|
service-unfreeze-on-demand nil
|
|
key-extend false
|
|
image-action nil
|
|
image-id nil
|
|
image-source-type nil
|
|
image-visibility nil
|
|
image-name nil
|
|
image-ports nil
|
|
mode :execute]
|
|
(cond
|
|
(empty? args)
|
|
(case mode
|
|
:session (session-command (or session-action :create) session-id session-shell network vcpu session-input-files)
|
|
:service (service-command (or service-action :create) service-id service-name service-ports service-bootstrap service-bootstrap-file service-type network vcpu service-input-files service-envs service-env-file service-unfreeze-on-demand)
|
|
:key (key-command key-extend)
|
|
:languages (languages-command false)
|
|
:image (image-command (or image-action :list) image-id image-source-type image-visibility image-name image-ports)
|
|
:execute (if file
|
|
(execute-command file env-vars artifacts out-dir network vcpu)
|
|
(do (println "Usage: un.clj [options] <source_file>")
|
|
(println " un.clj languages [--json]")
|
|
(println " un.clj session [options]")
|
|
(println " un.clj service [options]")
|
|
(println " un.clj service env <action> <service_id>")
|
|
(println " un.clj image [options]")
|
|
(println " un.clj key [options]")
|
|
(System/exit 1))))
|
|
|
|
(= (first args) "session")
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :session)
|
|
|
|
(= (first args) "service")
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :service)
|
|
|
|
(= (first args) "key")
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :key)
|
|
|
|
(= (first args) "languages")
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :languages)
|
|
|
|
(= (first args) "image")
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports :image)
|
|
|
|
;; Image options
|
|
(and (= mode :image) (or (= (first args) "--list") (= (first args) "-l")))
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :list image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--info"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :info (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--delete"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :delete (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--lock"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :lock (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--unlock"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :unlock (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--publish"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :publish (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--source-type"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id (second args) image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--visibility"))
|
|
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :visibility (second args) image-source-type (nth args 2) image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--spawn"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :spawn (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--clone"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend :clone (second args) image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--name"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility (second args) image-ports mode)
|
|
|
|
(and (= mode :image) (= (first args) "--ports"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name (second args) mode)
|
|
|
|
;; Key options
|
|
(and (= mode :key) (= (first args) "--extend"))
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand true image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
;; Languages options
|
|
(and (= mode :languages) (= (first args) "--json"))
|
|
(do
|
|
(languages-command true)
|
|
(System/exit 0))
|
|
|
|
;; Session options
|
|
(and (= mode :session) (= (first args) "--list"))
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu :list session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :session) (= (first args) "--kill"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu :kill (second args) session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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) session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :session) (= (first args) "-f"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell (conj session-input-files (second args))
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
:list service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
:info (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
:logs (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--freeze"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:sleep (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--unfreeze"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:wake (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
:destroy (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--resize"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:resize (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--set-unfreeze-on-demand"))
|
|
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:set-unfreeze-on-demand (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file (= (nth args 2) "true") key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--execute"))
|
|
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:execute (second args) service-name service-ports (nth args 2) service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--dump-bootstrap") (>= (count args) 3) (not (.startsWith (nth args 2) "-")))
|
|
(recur (rest (rest (rest args))) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:dump-bootstrap (second args) service-name service-ports service-bootstrap (nth args 2) service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--dump-bootstrap"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:dump-bootstrap (second args) service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
:create service-id (second args) service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
service-action service-id service-name (second args) service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
service-action service-id service-name service-ports (second args) service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--bootstrap-file"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap (second args) service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file (second args) service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "-f"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type (conj service-input-files (second args)) service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "env") (>= (count args) 2))
|
|
(let [env-action (second args)
|
|
env-target (when (and (>= (count args) 3) (not (.startsWith (nth args 2) "-"))) (nth args 2))
|
|
rest-args (if env-target (drop 3 args) (drop 2 args))]
|
|
(recur rest-args file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
:env env-action env-target service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))
|
|
|
|
(and (= mode :service) (= (first args) "-e"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files (conj service-envs (second args)) service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--env-file"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs (second args) service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(and (= mode :service) (= (first args) "--unfreeze-on-demand"))
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file (= (second args) "true") key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))
|
|
|
|
(= (first args) "-a")
|
|
(recur (rest args) file env-vars true out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(= (first args) "-o")
|
|
(recur (rest (rest args)) file env-vars artifacts (second args) network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
(= (first args) "-n")
|
|
(recur (rest (rest args)) file env-vars artifacts out-dir (second args) vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports 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 session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode)
|
|
|
|
;; Unknown option check
|
|
(and (= mode :session) (.startsWith (first args) "-"))
|
|
(do
|
|
(println (str red "Unknown option: " (first args) reset) *err*)
|
|
(println "Usage: un.clj session [options]")
|
|
(println "Options: --list, --kill ID, --shell SHELL, -s SHELL, -f FILE, -n NETWORK, -v VCPU")
|
|
(System/exit 1))
|
|
|
|
:else
|
|
(recur (rest args) file env-vars artifacts out-dir network vcpu session-action session-id session-shell session-input-files
|
|
service-action service-id service-name service-ports service-bootstrap service-bootstrap-file service-type service-input-files service-envs service-env-file service-unfreeze-on-demand key-extend image-action image-id image-source-type image-visibility image-name image-ports mode))))
|
|
|
|
(parse-args *command-line-args*)
|