Add key subcommand to remaining 25 implementations
Complete key command across all 42 CLI implementations: - un.go, un.v, un.fs, un.groovy (systems/JVM) - un.dart, un_inception.c, un_deno.ts, un.ps1 (mixed) - un.hs, un.ml, un.clj, un.scm (functional) - un.lisp, un.erl, un.ex, un.f90 (functional/scientific) - un.cob, un.pro, un.forth, un.raku (exotic) - un.m, un.awk (other) - un.js, un.ts, un.pl (fixes to existing) All implementations now support: - key: Validate API key and show status - key --extend: Open browser to renew key
This commit is contained in:
parent
5380af54cb
commit
fa40b3bc78
25 changed files with 1438 additions and 182 deletions
97
un.awk
97
un.awk
|
|
@ -44,6 +44,7 @@
|
|||
|
||||
BEGIN {
|
||||
API_BASE = "https://api.unsandbox.com"
|
||||
PORTAL_BASE = "https://unsandbox.com"
|
||||
|
||||
# Extension to language map
|
||||
split("py:python js:javascript ts:typescript rb:ruby php:php pl:perl lua:lua sh:bash go:go rs:rust c:c cpp:cpp java:java kt:kotlin cs:csharp fs:fsharp hs:haskell ml:ocaml clj:clojure scm:scheme lisp:commonlisp erl:erlang ex:elixir jl:julia r:r cr:crystal d:d nim:nim zig:zig v:v dart:dart groovy:groovy f90:fortran cob:cobol pro:prolog forth:forth tcl:tcl raku:raku m:objc awk:awk ps1:powershell", pairs, " ")
|
||||
|
|
@ -242,10 +243,97 @@ function service_create(name, ports, domains, service_type, bootstrap) {
|
|||
print response
|
||||
}
|
||||
|
||||
function validate_key(api_key, do_extend) {
|
||||
# Call curl to validate key
|
||||
cmd = "curl -s -X POST '" PORTAL_BASE "/keys/validate' " \
|
||||
"-H 'Content-Type: application/json' " \
|
||||
"-H 'Authorization: Bearer " api_key "'"
|
||||
|
||||
response = ""
|
||||
while ((cmd | getline line) > 0) {
|
||||
response = response line
|
||||
}
|
||||
close(cmd)
|
||||
|
||||
# Parse expired status (simple regex check)
|
||||
if (match(response, /"expired":true/)) {
|
||||
print RED "Expired" RESET
|
||||
|
||||
# Extract public_key if present
|
||||
if (match(response, /"public_key":"([^"]+)"/, arr)) {
|
||||
public_key = arr[1]
|
||||
print "Public Key: " public_key
|
||||
}
|
||||
|
||||
# Extract tier
|
||||
if (match(response, /"tier":"([^"]+)"/, arr)) {
|
||||
print "Tier: " arr[1]
|
||||
}
|
||||
|
||||
# Extract expires_at
|
||||
if (match(response, /"expires_at":"([^"]+)"/, arr)) {
|
||||
print "Expired: " arr[1]
|
||||
}
|
||||
|
||||
print YELLOW "To renew: Visit https://unsandbox.com/keys/extend" RESET
|
||||
|
||||
if (do_extend && public_key) {
|
||||
url = PORTAL_BASE "/keys/extend?pk=" public_key
|
||||
print ""
|
||||
print BLUE "Opening browser to: " url RESET
|
||||
system("xdg-open '" url "' 2>/dev/null || open '" url "' 2>/dev/null &")
|
||||
}
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Valid key
|
||||
print GREEN "Valid" RESET
|
||||
|
||||
# Extract and display fields
|
||||
if (match(response, /"public_key":"([^"]+)"/, arr)) {
|
||||
public_key = arr[1]
|
||||
print "Public Key: " public_key
|
||||
}
|
||||
if (match(response, /"tier":"([^"]+)"/, arr)) {
|
||||
print "Tier: " arr[1]
|
||||
}
|
||||
if (match(response, /"status":"([^"]+)"/, arr)) {
|
||||
print "Status: " arr[1]
|
||||
}
|
||||
if (match(response, /"expires_at":"([^"]+)"/, arr)) {
|
||||
print "Expires: " arr[1]
|
||||
}
|
||||
if (match(response, /"time_remaining":"([^"]+)"/, arr)) {
|
||||
print "Time Remaining: " arr[1]
|
||||
}
|
||||
if (match(response, /"rate_limit":"?([^",}]+)"?/, arr)) {
|
||||
print "Rate Limit: " arr[1]
|
||||
}
|
||||
if (match(response, /"burst":"?([^",}]+)"?/, arr)) {
|
||||
print "Burst: " arr[1]
|
||||
}
|
||||
if (match(response, /"concurrency":"?([^",}]+)"?/, arr)) {
|
||||
print "Concurrency: " arr[1]
|
||||
}
|
||||
|
||||
if (do_extend && public_key) {
|
||||
url = PORTAL_BASE "/keys/extend?pk=" public_key
|
||||
print ""
|
||||
print BLUE "Opening browser to: " url RESET
|
||||
system("xdg-open '" url "' 2>/dev/null || open '" url "' 2>/dev/null &")
|
||||
}
|
||||
}
|
||||
|
||||
function cmd_key(do_extend) {
|
||||
api_key = get_api_key()
|
||||
validate_key(api_key, do_extend)
|
||||
}
|
||||
|
||||
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 key [--extend]"
|
||||
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"
|
||||
|
|
@ -288,6 +376,15 @@ END {
|
|||
exit 0
|
||||
}
|
||||
|
||||
if (ARGV[1] == "key") {
|
||||
do_extend = 0
|
||||
if (ARGC >= 3 && ARGV[2] == "--extend") {
|
||||
do_extend = 1
|
||||
}
|
||||
cmd_key(do_extend)
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (ARGV[1] == "service") {
|
||||
if (ARGC >= 3 && ARGV[2] == "--list") {
|
||||
service_list()
|
||||
|
|
|
|||
32
un.clj
32
un.clj
|
|
@ -216,27 +216,39 @@
|
|||
status (extract-field "status" response)
|
||||
public-key (extract-field "public_key" response)
|
||||
tier (extract-field "tier" response)
|
||||
expires-at (extract-field "expires_at" 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))
|
||||
(when public-key (println (str "Public Key: " public-key)))
|
||||
(when tier (println (str "Tier: " tier)))
|
||||
(when expires-at (println (str "Expires: " expires-at)))
|
||||
(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))
|
||||
(when public-key (println (str "Public Key: " public-key)))
|
||||
(when tier (println (str "Tier: " tier)))
|
||||
(when expires-at (println (str "Expired: " expires-at)))
|
||||
(println (str yellow "To renew: Visit " portal-base "/keys/extend" reset))
|
||||
(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
|
||||
|
|
|
|||
98
un.cob
98
un.cob
|
|
@ -73,6 +73,9 @@
|
|||
01 WS-DOMAINS PIC X(256).
|
||||
01 WS-SERVICE-TYPE PIC X(64).
|
||||
01 WS-BOOTSTRAP PIC X(2048).
|
||||
01 WS-PORTAL-BASE PIC X(256) VALUE
|
||||
"https://unsandbox.com".
|
||||
01 WS-EXTEND-FLAG PIC X(8).
|
||||
|
||||
PROCEDURE DIVISION.
|
||||
MAIN-PROCEDURE.
|
||||
|
|
@ -98,6 +101,11 @@
|
|||
STOP RUN
|
||||
END-IF.
|
||||
|
||||
IF WS-ARG1 = "key"
|
||||
PERFORM HANDLE-KEY
|
||||
STOP RUN
|
||||
END-IF.
|
||||
|
||||
* Default: execute command
|
||||
MOVE WS-ARG1 TO WS-FILENAME.
|
||||
PERFORM HANDLE-EXECUTE.
|
||||
|
|
@ -442,3 +450,93 @@
|
|||
END-STRING.
|
||||
|
||||
CALL "SYSTEM" USING WS-CURL-CMD.
|
||||
|
||||
HANDLE-KEY.
|
||||
* Get API key
|
||||
ACCEPT WS-API-KEY FROM ENVIRONMENT "UNSANDBOX_API_KEY".
|
||||
IF WS-API-KEY = SPACES
|
||||
DISPLAY "Error: UNSANDBOX_API_KEY not set" UPON SYSERR
|
||||
MOVE 1 TO RETURN-CODE
|
||||
STOP RUN
|
||||
END-IF.
|
||||
|
||||
* Parse key arguments
|
||||
MOVE SPACES TO WS-EXTEND-FLAG.
|
||||
ACCEPT WS-ARG2 FROM ARGUMENT-VALUE.
|
||||
|
||||
IF WS-ARG2 = "--extend"
|
||||
MOVE "true" TO WS-EXTEND-FLAG
|
||||
END-IF.
|
||||
|
||||
* Validate key
|
||||
PERFORM VALIDATE-KEY.
|
||||
|
||||
VALIDATE-KEY.
|
||||
* Build curl command to validate API key
|
||||
STRING "curl -s -X POST "
|
||||
FUNCTION TRIM(WS-PORTAL-BASE)
|
||||
"/keys/validate "
|
||||
"-H 'Content-Type: application/json' "
|
||||
"-H 'Authorization: Bearer " FUNCTION TRIM(WS-API-KEY)
|
||||
"' -o /tmp/unsandbox_key_resp.json; "
|
||||
"STATUS=$?; "
|
||||
"if [ $STATUS -ne 0 ]; then "
|
||||
"echo -e '\x1b[31mInvalid\x1b[0m'; "
|
||||
"exit 1; "
|
||||
"fi; "
|
||||
"EXPIRED=$(jq -r '.expired // false' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"PUBLIC_KEY=$(jq -r '.public_key // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
DELIMITED BY SIZE INTO WS-CURL-CMD
|
||||
END-STRING.
|
||||
|
||||
IF WS-EXTEND-FLAG = "true"
|
||||
STRING FUNCTION TRIM(WS-CURL-CMD)
|
||||
"xdg-open '"
|
||||
FUNCTION TRIM(WS-PORTAL-BASE)
|
||||
"/keys/extend?pk='\"$PUBLIC_KEY\" 2>/dev/null; "
|
||||
DELIMITED BY SIZE INTO WS-CURL-CMD
|
||||
END-STRING
|
||||
ELSE
|
||||
STRING FUNCTION TRIM(WS-CURL-CMD)
|
||||
"if [ \"$EXPIRED\" = \"true\" ]; then "
|
||||
"echo -e '\x1b[31mExpired\x1b[0m'; "
|
||||
"echo 'Public Key: '$PUBLIC_KEY; "
|
||||
"echo 'Tier: '$(jq -r '.tier // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Expired: '$(jq -r '.expires_at // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo -e '\x1b[33mTo renew: Visit "
|
||||
"https://unsandbox.com/keys/extend\x1b[0m'; "
|
||||
"rm -f /tmp/unsandbox_key_resp.json; "
|
||||
"exit 1; "
|
||||
"else "
|
||||
"echo -e '\x1b[32mValid\x1b[0m'; "
|
||||
"echo 'Public Key: '$PUBLIC_KEY; "
|
||||
"echo 'Tier: '$(jq -r '.tier // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Status: '$(jq -r '.status // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Expires: '$(jq -r '.expires_at // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Time Remaining: '$(jq -r "
|
||||
"'.time_remaining // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Rate Limit: '$(jq -r '.rate_limit // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Burst: '$(jq -r '.burst // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"echo 'Concurrency: '$(jq -r '.concurrency // \"N/A\"' "
|
||||
"/tmp/unsandbox_key_resp.json); "
|
||||
"fi; "
|
||||
DELIMITED BY SIZE INTO WS-CURL-CMD
|
||||
END-STRING
|
||||
END-IF.
|
||||
|
||||
STRING FUNCTION TRIM(WS-CURL-CMD)
|
||||
"rm -f /tmp/unsandbox_key_resp.json"
|
||||
DELIMITED BY SIZE INTO WS-CURL-CMD
|
||||
END-STRING.
|
||||
|
||||
CALL "SYSTEM" USING WS-CURL-CMD.
|
||||
|
|
|
|||
73
un.dart
73
un.dart
|
|
@ -357,41 +357,52 @@ Future<void> cmdKey(Args args) async {
|
|||
try {
|
||||
final result = await apiRequestCurl('/keys/validate', 'POST', null, apiKey, baseUrl: portalBase);
|
||||
|
||||
final status = result['status'] as String?;
|
||||
final publicKey = result['public_key'] as String?;
|
||||
final tier = result['tier'] as String?;
|
||||
final expiresAt = result['expires_at'] as String?;
|
||||
|
||||
if (status == 'valid') {
|
||||
print('${green}Valid$reset');
|
||||
if (publicKey != null) print('Public Key: $publicKey');
|
||||
if (tier != null) print('Tier: $tier');
|
||||
if (expiresAt != null) print('Expires: $expiresAt');
|
||||
} else if (status == 'expired') {
|
||||
print('${red}Expired$reset');
|
||||
if (publicKey != null) print('Public Key: $publicKey');
|
||||
if (tier != null) print('Tier: $tier');
|
||||
if (expiresAt != null) print('Expired: $expiresAt');
|
||||
print('${yellow}To renew: Visit $portalBase/keys/extend$reset');
|
||||
} else {
|
||||
print('${red}Invalid$reset');
|
||||
}
|
||||
|
||||
if (args.keyExtend && publicKey != null) {
|
||||
final url = '$portalBase/keys/extend?pk=$publicKey';
|
||||
print('${yellow}Opening: $url$reset');
|
||||
if (Platform.isMacOS) {
|
||||
await Process.run('open', [url]);
|
||||
} else if (Platform.isLinux) {
|
||||
await Process.run('xdg-open', [url]);
|
||||
} else if (Platform.isWindows) {
|
||||
await Process.run('cmd', ['/c', 'start', url]);
|
||||
// Handle --extend flag
|
||||
if (args.keyExtend) {
|
||||
final publicKey = result['public_key'] as String?;
|
||||
if (publicKey != null) {
|
||||
final url = '$portalBase/keys/extend?pk=$publicKey';
|
||||
print('${blue}Opening browser to extend key...$reset');
|
||||
if (Platform.isMacOS) {
|
||||
await Process.run('open', [url]);
|
||||
} else if (Platform.isLinux) {
|
||||
await Process.run('xdg-open', [url]);
|
||||
} else if (Platform.isWindows) {
|
||||
await Process.run('cmd', ['/c', 'start', url]);
|
||||
} else {
|
||||
print('${yellow}Please open manually: $url$reset');
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
print('${yellow}Please open manually: $url$reset');
|
||||
stderr.writeln('${red}Error: Could not retrieve public key$reset');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if key is expired
|
||||
final expired = result['expired'] as bool? ?? false;
|
||||
if (expired) {
|
||||
print('${red}Expired$reset');
|
||||
print('Public Key: ${result['public_key'] ?? 'N/A'}');
|
||||
print('Tier: ${result['tier'] ?? 'N/A'}');
|
||||
print('Expired: ${result['expires_at'] ?? 'N/A'}');
|
||||
print('${yellow}To renew: Visit $portalBase/keys/extend$reset');
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Valid key
|
||||
print('${green}Valid$reset');
|
||||
print('Public Key: ${result['public_key'] ?? 'N/A'}');
|
||||
print('Tier: ${result['tier'] ?? 'N/A'}');
|
||||
print('Status: ${result['status'] ?? 'N/A'}');
|
||||
print('Expires: ${result['expires_at'] ?? 'N/A'}');
|
||||
print('Time Remaining: ${result['time_remaining'] ?? 'N/A'}');
|
||||
print('Rate Limit: ${result['rate_limit'] ?? 'N/A'}');
|
||||
print('Burst: ${result['burst'] ?? 'N/A'}');
|
||||
print('Concurrency: ${result['concurrency'] ?? 'N/A'}');
|
||||
} catch (e) {
|
||||
stderr.writeln('${red}Error validating key: $e$reset');
|
||||
print('${red}Invalid$reset');
|
||||
print('Reason: $e');
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
9
un.erl
9
un.erl
|
|
@ -201,13 +201,22 @@ parse_and_display_key_status(Response, ShouldExtend) ->
|
|||
PublicKey = extract_json_field(Response, "public_key"),
|
||||
Tier = extract_json_field(Response, "tier"),
|
||||
ExpiresAt = extract_json_field(Response, "expires_at"),
|
||||
TimeRemaining = extract_json_field(Response, "time_remaining"),
|
||||
RateLimit = extract_json_field(Response, "rate_limit"),
|
||||
Burst = extract_json_field(Response, "burst"),
|
||||
Concurrency = extract_json_field(Response, "concurrency"),
|
||||
|
||||
case Status of
|
||||
"valid" ->
|
||||
io:format("\033[32mValid\033[0m~n"),
|
||||
io:format("Public Key: ~s~n", [PublicKey]),
|
||||
io:format("Tier: ~s~n", [Tier]),
|
||||
io:format("Status: ~s~n", [Status]),
|
||||
io:format("Expires: ~s~n", [ExpiresAt]),
|
||||
if TimeRemaining =/= "" -> io:format("Time Remaining: ~s~n", [TimeRemaining]); true -> ok end,
|
||||
if RateLimit =/= "" -> io:format("Rate Limit: ~s~n", [RateLimit]); true -> ok end,
|
||||
if Burst =/= "" -> io:format("Burst: ~s~n", [Burst]); true -> ok end,
|
||||
if Concurrency =/= "" -> io:format("Concurrency: ~s~n", [Concurrency]); true -> ok end,
|
||||
if ShouldExtend ->
|
||||
open_extend_page(PublicKey);
|
||||
true -> ok
|
||||
|
|
|
|||
18
un.ex
18
un.ex
|
|
@ -248,13 +248,22 @@ defmodule Un do
|
|||
public_key = Map.get(data, "public_key")
|
||||
tier = Map.get(data, "tier")
|
||||
expires_at = Map.get(data, "expires_at")
|
||||
time_remaining = Map.get(data, "time_remaining")
|
||||
rate_limit = Map.get(data, "rate_limit")
|
||||
burst = Map.get(data, "burst")
|
||||
concurrency = Map.get(data, "concurrency")
|
||||
|
||||
case status do
|
||||
"valid" ->
|
||||
IO.puts("#{@green}Valid#{@reset}")
|
||||
IO.puts("Public Key: #{public_key}")
|
||||
IO.puts("Tier: #{tier}")
|
||||
IO.puts("Status: #{status}")
|
||||
IO.puts("Expires: #{expires_at}")
|
||||
if time_remaining, do: IO.puts("Time Remaining: #{time_remaining}")
|
||||
if rate_limit, do: IO.puts("Rate Limit: #{rate_limit}")
|
||||
if burst, do: IO.puts("Burst: #{burst}")
|
||||
if concurrency, do: IO.puts("Concurrency: #{concurrency}")
|
||||
|
||||
if extend do
|
||||
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
|
||||
|
|
@ -285,13 +294,22 @@ defmodule Un do
|
|||
public_key = extract_json_value(response, "public_key")
|
||||
tier = extract_json_value(response, "tier")
|
||||
expires_at = extract_json_value(response, "expires_at")
|
||||
time_remaining = extract_json_value(response, "time_remaining")
|
||||
rate_limit = extract_json_value(response, "rate_limit")
|
||||
burst = extract_json_value(response, "burst")
|
||||
concurrency = extract_json_value(response, "concurrency")
|
||||
|
||||
case status do
|
||||
"valid" ->
|
||||
IO.puts("#{@green}Valid#{@reset}")
|
||||
IO.puts("Public Key: #{public_key}")
|
||||
IO.puts("Tier: #{tier}")
|
||||
IO.puts("Status: #{status}")
|
||||
IO.puts("Expires: #{expires_at}")
|
||||
if time_remaining, do: IO.puts("Time Remaining: #{time_remaining}")
|
||||
if rate_limit, do: IO.puts("Rate Limit: #{rate_limit}")
|
||||
if burst, do: IO.puts("Burst: #{burst}")
|
||||
if concurrency, do: IO.puts("Concurrency: #{concurrency}")
|
||||
|
||||
if extend do
|
||||
open_browser("#{@portal_base}/keys/extend?pk=#{public_key}")
|
||||
|
|
|
|||
108
un.f90
108
un.f90
|
|
@ -41,13 +41,14 @@ program unsandbox_cli
|
|||
character(len=1024) :: filename, language, api_key, ext, arg, subcommand
|
||||
character(len=256) :: session_id, service_id
|
||||
integer :: stat, i, nargs, dot_pos
|
||||
logical :: list_flag, is_session, is_service
|
||||
logical :: list_flag, is_session, is_service, is_key
|
||||
|
||||
! Initialize
|
||||
subcommand = ''
|
||||
list_flag = .false.
|
||||
is_session = .false.
|
||||
is_service = .false.
|
||||
is_key = .false.
|
||||
session_id = ''
|
||||
service_id = ''
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ program unsandbox_cli
|
|||
write(0, '(A)') 'Usage: un.f90 [options] <source_file>'
|
||||
write(0, '(A)') ' un.f90 session [options]'
|
||||
write(0, '(A)') ' un.f90 service [options]'
|
||||
write(0, '(A)') ' un.f90 key [--extend]'
|
||||
stop 1
|
||||
end if
|
||||
|
||||
|
|
@ -70,6 +72,10 @@ program unsandbox_cli
|
|||
is_service = .true.
|
||||
call handle_service()
|
||||
stop 0
|
||||
else if (trim(arg) == 'key') then
|
||||
is_key = .true.
|
||||
call handle_key()
|
||||
stop 0
|
||||
else
|
||||
! Default execute command
|
||||
filename = trim(arg)
|
||||
|
|
@ -314,4 +320,104 @@ contains
|
|||
end if
|
||||
end subroutine handle_service
|
||||
|
||||
subroutine handle_key()
|
||||
character(len=4096) :: full_cmd
|
||||
character(len=256) :: arg
|
||||
integer :: i, stat
|
||||
logical :: extend_mode
|
||||
character(len=32) :: portal_base
|
||||
|
||||
portal_base = 'https://unsandbox.com'
|
||||
extend_mode = .false.
|
||||
|
||||
! Check for --extend flag
|
||||
do i = 2, command_argument_count()
|
||||
call get_command_argument(i, arg)
|
||||
if (trim(arg) == '--extend') then
|
||||
extend_mode = .true.
|
||||
end if
|
||||
end do
|
||||
|
||||
! Get API key
|
||||
call get_environment_variable('UNSANDBOX_API_KEY', api_key, status=stat)
|
||||
if (stat /= 0 .or. len_trim(api_key) == 0) then
|
||||
write(0, '(A)') 'Error: UNSANDBOX_API_KEY not set'
|
||||
stop 1
|
||||
end if
|
||||
|
||||
if (extend_mode) then
|
||||
! Validate and extend
|
||||
write(full_cmd, '(30A)') &
|
||||
'resp=$(curl -s -X POST ', trim(portal_base), '/keys/validate ', &
|
||||
'-H "Content-Type: application/json" ', &
|
||||
'-H "Authorization: Bearer ', trim(api_key), '" ', &
|
||||
'-d "{}"); ', &
|
||||
'status=$(echo "$resp" | jq -r ".status // empty"); ', &
|
||||
'public_key=$(echo "$resp" | jq -r ".public_key // empty"); ', &
|
||||
'tier=$(echo "$resp" | jq -r ".tier // empty"); ', &
|
||||
'expires_at=$(echo "$resp" | jq -r ".expires_at // empty"); ', &
|
||||
'time_remaining=$(echo "$resp" | jq -r ".time_remaining // empty"); ', &
|
||||
'rate_limit=$(echo "$resp" | jq -r ".rate_limit // empty"); ', &
|
||||
'burst=$(echo "$resp" | jq -r ".burst // empty"); ', &
|
||||
'concurrency=$(echo "$resp" | jq -r ".concurrency // empty"); ', &
|
||||
'if [ "$status" = "valid" ]; then ', &
|
||||
'echo -e "\x1b[32mValid\x1b[0m"; ', &
|
||||
'echo "Public Key: $public_key"; ', &
|
||||
'echo "Tier: $tier"; ', &
|
||||
'echo "Status: $status"; ', &
|
||||
'echo "Expires: $expires_at"; ', &
|
||||
'[ -n "$time_remaining" ] && echo "Time Remaining: $time_remaining"; ', &
|
||||
'[ -n "$rate_limit" ] && echo "Rate Limit: $rate_limit"; ', &
|
||||
'[ -n "$burst" ] && echo "Burst: $burst"; ', &
|
||||
'[ -n "$concurrency" ] && echo "Concurrency: $concurrency"; ', &
|
||||
'echo -e "\x1b[34mOpening browser to extend key...\x1b[0m"; ', &
|
||||
'xdg-open "', trim(portal_base), '/keys/extend?pk=$public_key" 2>/dev/null || ', &
|
||||
'sensible-browser "', trim(portal_base), '/keys/extend?pk=$public_key" 2>/dev/null &; ', &
|
||||
'elif [ "$status" = "expired" ]; then ', &
|
||||
'echo -e "\x1b[31mExpired\x1b[0m"; ', &
|
||||
'echo "Public Key: $public_key"; ', &
|
||||
'echo "Tier: $tier"; ', &
|
||||
'echo "Expired: $expires_at"; ', &
|
||||
'echo -e "\x1b[33mTo renew: Visit ', trim(portal_base), '/keys/extend\x1b[0m"; ', &
|
||||
'echo -e "\x1b[34mOpening browser to extend key...\x1b[0m"; ', &
|
||||
'xdg-open "', trim(portal_base), '/keys/extend?pk=$public_key" 2>/dev/null || ', &
|
||||
'sensible-browser "', trim(portal_base), '/keys/extend?pk=$public_key" 2>/dev/null &; ', &
|
||||
'else echo -e "\x1b[31mInvalid\x1b[0m"; fi'
|
||||
else
|
||||
! Validate only
|
||||
write(full_cmd, '(30A)') &
|
||||
'resp=$(curl -s -X POST ', trim(portal_base), '/keys/validate ', &
|
||||
'-H "Content-Type: application/json" ', &
|
||||
'-H "Authorization: Bearer ', trim(api_key), '" ', &
|
||||
'-d "{}"); ', &
|
||||
'status=$(echo "$resp" | jq -r ".status // empty"); ', &
|
||||
'public_key=$(echo "$resp" | jq -r ".public_key // empty"); ', &
|
||||
'tier=$(echo "$resp" | jq -r ".tier // empty"); ', &
|
||||
'expires_at=$(echo "$resp" | jq -r ".expires_at // empty"); ', &
|
||||
'time_remaining=$(echo "$resp" | jq -r ".time_remaining // empty"); ', &
|
||||
'rate_limit=$(echo "$resp" | jq -r ".rate_limit // empty"); ', &
|
||||
'burst=$(echo "$resp" | jq -r ".burst // empty"); ', &
|
||||
'concurrency=$(echo "$resp" | jq -r ".concurrency // empty"); ', &
|
||||
'if [ "$status" = "valid" ]; then ', &
|
||||
'echo -e "\x1b[32mValid\x1b[0m"; ', &
|
||||
'echo "Public Key: $public_key"; ', &
|
||||
'echo "Tier: $tier"; ', &
|
||||
'echo "Status: $status"; ', &
|
||||
'echo "Expires: $expires_at"; ', &
|
||||
'[ -n "$time_remaining" ] && echo "Time Remaining: $time_remaining"; ', &
|
||||
'[ -n "$rate_limit" ] && echo "Rate Limit: $rate_limit"; ', &
|
||||
'[ -n "$burst" ] && echo "Burst: $burst"; ', &
|
||||
'[ -n "$concurrency" ] && echo "Concurrency: $concurrency"; ', &
|
||||
'elif [ "$status" = "expired" ]; then ', &
|
||||
'echo -e "\x1b[31mExpired\x1b[0m"; ', &
|
||||
'echo "Public Key: $public_key"; ', &
|
||||
'echo "Tier: $tier"; ', &
|
||||
'echo "Expired: $expires_at"; ', &
|
||||
'echo -e "\x1b[33mTo renew: Visit ', trim(portal_base), '/keys/extend\x1b[0m"; ', &
|
||||
'else echo -e "\x1b[31mInvalid\x1b[0m"; fi'
|
||||
end if
|
||||
|
||||
call execute_command_line(trim(full_cmd), wait=.true., exitstat=stat)
|
||||
end subroutine handle_key
|
||||
|
||||
end program unsandbox_cli
|
||||
|
|
|
|||
83
un.forth
83
un.forth
|
|
@ -39,6 +39,12 @@
|
|||
\ Usage: gforth un.forth <source_file>
|
||||
\ gforth un.forth session [options]
|
||||
\ gforth un.forth service [options]
|
||||
\ gforth un.forth key [options]
|
||||
|
||||
\ Constants
|
||||
: portal-base ( -- addr len )
|
||||
s" https://unsandbox.com"
|
||||
;
|
||||
|
||||
\ Extension to language mapping (simple linear search)
|
||||
: ext-lang ( addr len -- addr len | 0 0 )
|
||||
|
|
@ -282,6 +288,77 @@
|
|||
s" chmod +x /tmp/unsandbox_cmd.sh && bash /tmp/unsandbox_cmd.sh && rm -f /tmp/unsandbox_cmd.sh" system
|
||||
;
|
||||
|
||||
\ Key validate
|
||||
: validate-key ( extend-flag -- )
|
||||
get-api-key
|
||||
s" /tmp/unsandbox_key_cmd.sh" w/o create-file throw >r
|
||||
s" #!/bin/bash" r@ write-line throw
|
||||
s" API_KEY='" r@ write-file throw
|
||||
get-api-key r@ write-file throw
|
||||
s" '" r@ write-line throw
|
||||
s" PORTAL_BASE='" r@ write-file throw
|
||||
portal-base r@ write-file throw
|
||||
s" '" r@ write-line throw
|
||||
|
||||
\ Check if extend flag is set
|
||||
0= if
|
||||
\ Normal validation
|
||||
s" curl -s -X POST $PORTAL_BASE/keys/validate -H 'Content-Type: application/json' -H \"Authorization: Bearer $API_KEY\" -o /tmp/unsandbox_key_resp.json" r@ write-line throw
|
||||
s" STATUS=$?" r@ write-line throw
|
||||
s" if [ $STATUS -ne 0 ]; then" r@ write-line throw
|
||||
s" echo -e '\\x1b[31mInvalid\\x1b[0m'" r@ write-line throw
|
||||
s" exit 1" r@ write-line throw
|
||||
s" fi" r@ write-line throw
|
||||
s" EXPIRED=$(jq -r '.expired // false' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" if [ \"$EXPIRED\" = \"true\" ]; then" r@ write-line throw
|
||||
s" echo -e '\\x1b[31mExpired\\x1b[0m'" r@ write-line throw
|
||||
s" echo 'Public Key: '$(jq -r '.public_key // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Tier: '$(jq -r '.tier // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Expired: '$(jq -r '.expires_at // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo -e '\\x1b[33mTo renew: Visit https://unsandbox.com/keys/extend\\x1b[0m'" r@ write-line throw
|
||||
s" rm -f /tmp/unsandbox_key_resp.json" r@ write-line throw
|
||||
s" exit 1" r@ write-line throw
|
||||
s" else" r@ write-line throw
|
||||
s" echo -e '\\x1b[32mValid\\x1b[0m'" r@ write-line throw
|
||||
s" echo 'Public Key: '$(jq -r '.public_key // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Tier: '$(jq -r '.tier // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Status: '$(jq -r '.status // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Expires: '$(jq -r '.expires_at // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Time Remaining: '$(jq -r '.time_remaining // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Rate Limit: '$(jq -r '.rate_limit // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Burst: '$(jq -r '.burst // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" echo 'Concurrency: '$(jq -r '.concurrency // \"N/A\"' /tmp/unsandbox_key_resp.json)" r@ write-line throw
|
||||
s" fi" r@ write-line throw
|
||||
s" rm -f /tmp/unsandbox_key_resp.json" r@ write-line throw
|
||||
else
|
||||
\ Extend mode
|
||||
s" RESP=$(curl -s -X POST $PORTAL_BASE/keys/validate -H 'Content-Type: application/json' -H \"Authorization: Bearer $API_KEY\")" r@ write-line throw
|
||||
s" PUBLIC_KEY=$(echo \"$RESP\" | jq -r '.public_key // \"N/A\"')" r@ write-line throw
|
||||
s" xdg-open \"$PORTAL_BASE/keys/extend?pk=$PUBLIC_KEY\" 2>/dev/null" r@ write-line throw
|
||||
then
|
||||
|
||||
r> close-file throw
|
||||
s" chmod +x /tmp/unsandbox_key_cmd.sh && /tmp/unsandbox_key_cmd.sh && rm -f /tmp/unsandbox_key_cmd.sh" system
|
||||
;
|
||||
|
||||
\ Handle key subcommand
|
||||
: handle-key ( -- )
|
||||
argc @ 3 < if
|
||||
0 validate-key
|
||||
0 (bye)
|
||||
then
|
||||
|
||||
2 arg 2dup s" --extend" compare 0= if
|
||||
2drop
|
||||
1 validate-key
|
||||
0 (bye)
|
||||
then
|
||||
|
||||
2drop
|
||||
0 validate-key
|
||||
0 (bye)
|
||||
;
|
||||
|
||||
\ Handle session subcommand
|
||||
: handle-session ( -- )
|
||||
argc @ 3 < if
|
||||
|
|
@ -398,6 +475,7 @@
|
|||
s" Usage: gforth un.forth <source_file>" type cr
|
||||
s" gforth un.forth session [options]" type cr
|
||||
s" gforth un.forth service [options]" type cr
|
||||
s" gforth un.forth key [options]" type cr
|
||||
1 (bye)
|
||||
then
|
||||
|
||||
|
|
@ -415,6 +493,11 @@
|
|||
0 (bye)
|
||||
then
|
||||
|
||||
2dup s" key" compare 0= if
|
||||
2drop handle-key
|
||||
0 (bye)
|
||||
then
|
||||
|
||||
\ Default: execute file
|
||||
execute-file
|
||||
;
|
||||
|
|
|
|||
92
un.fs
92
un.fs
|
|
@ -46,6 +46,7 @@ open System.Net
|
|||
open System.Text
|
||||
|
||||
let apiBase = "https://api.unsandbox.com"
|
||||
let portalBase = "https://unsandbox.com"
|
||||
let blue = "\x1B[34m"
|
||||
let red = "\x1B[31m"
|
||||
let green = "\x1B[32m"
|
||||
|
|
@ -93,6 +94,7 @@ type Args = {
|
|||
mutable ServiceSleep: string option
|
||||
mutable ServiceWake: string option
|
||||
mutable ServiceDestroy: string option
|
||||
mutable KeyExtend: bool
|
||||
}
|
||||
|
||||
let getApiKey (argsKey: string option) =
|
||||
|
|
@ -324,6 +326,87 @@ let cmdSession (args: Args) =
|
|||
| None -> printfn "%sSession created%s" green reset
|
||||
printfn "%s(Interactive sessions require WebSocket - use un2 for full support)%s" yellow reset
|
||||
|
||||
let openBrowser (url: string) =
|
||||
try
|
||||
let os = Environment.OSVersion.Platform
|
||||
let cmd =
|
||||
if os = PlatformID.Unix || os = PlatformID.MacOSX then
|
||||
if System.IO.File.Exists("/usr/bin/xdg-open") then
|
||||
System.Diagnostics.Process.Start("xdg-open", url)
|
||||
else
|
||||
System.Diagnostics.Process.Start("open", url)
|
||||
else
|
||||
System.Diagnostics.Process.Start("cmd", sprintf "/c start %s" url)
|
||||
cmd.WaitForExit()
|
||||
with ex ->
|
||||
eprintfn "%sError opening browser: %s%s" red ex.Message reset
|
||||
|
||||
let cmdKey (args: Args) =
|
||||
let apiKey = getApiKey args.ApiKey
|
||||
|
||||
ServicePointManager.SecurityProtocol <- SecurityProtocolType.Tls12 ||| SecurityProtocolType.Tls11 ||| SecurityProtocolType.Tls
|
||||
|
||||
let request = WebRequest.Create(portalBase + "/keys/validate") :?> HttpWebRequest
|
||||
request.Method <- "POST"
|
||||
request.ContentType <- "application/json"
|
||||
request.Headers.Add("Authorization", sprintf "Bearer %s" apiKey)
|
||||
request.Timeout <- 30000
|
||||
|
||||
try
|
||||
use response = request.GetResponse() :?> HttpWebResponse
|
||||
use reader = new StreamReader(response.GetResponseStream())
|
||||
let responseText = reader.ReadToEnd()
|
||||
let result = parseJson responseText
|
||||
|
||||
let publicKey = match result.TryFind "public_key" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let tier = match result.TryFind "tier" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let status = match result.TryFind "status" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let expiresAt = match result.TryFind "expires_at" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let timeRemaining = match result.TryFind "time_remaining" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let rateLimit = match result.TryFind "rate_limit" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let burst = match result.TryFind "burst" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let concurrency = match result.TryFind "concurrency" with | Some v -> v.ToString() | None -> "N/A"
|
||||
let expired = match result.TryFind "expired" with | Some v -> v.ToString() = "True" | None -> false
|
||||
|
||||
if args.KeyExtend && publicKey <> "N/A" then
|
||||
let extendUrl = sprintf "%s/keys/extend?pk=%s" portalBase publicKey
|
||||
printfn "%sOpening browser to extend key...%s" blue reset
|
||||
openBrowser extendUrl
|
||||
elif expired then
|
||||
printfn "%sExpired%s" red reset
|
||||
printfn "Public Key: %s" publicKey
|
||||
printfn "Tier: %s" tier
|
||||
printfn "Expired: %s" expiresAt
|
||||
printfn "%sTo renew: Visit https://unsandbox.com/keys/extend%s" yellow reset
|
||||
exit 1
|
||||
else
|
||||
printfn "%sValid%s" green reset
|
||||
printfn "Public Key: %s" publicKey
|
||||
printfn "Tier: %s" tier
|
||||
printfn "Status: %s" status
|
||||
printfn "Expires: %s" expiresAt
|
||||
printfn "Time Remaining: %s" timeRemaining
|
||||
printfn "Rate Limit: %s" rateLimit
|
||||
printfn "Burst: %s" burst
|
||||
printfn "Concurrency: %s" concurrency
|
||||
with
|
||||
| :? WebException as ex ->
|
||||
printfn "%sInvalid%s" red reset
|
||||
let errorMsg =
|
||||
if ex.Response <> null then
|
||||
use reader = new StreamReader(ex.Response.GetResponseStream())
|
||||
let body = reader.ReadToEnd()
|
||||
try
|
||||
let errorResult = parseJson body
|
||||
match errorResult.TryFind "error" with
|
||||
| Some err -> err.ToString()
|
||||
| None -> body
|
||||
with _ -> body
|
||||
else
|
||||
ex.Message
|
||||
printfn "Reason: %s" errorMsg
|
||||
exit 1
|
||||
|
||||
let cmdService (args: Args) =
|
||||
let apiKey = getApiKey args.ApiKey
|
||||
|
||||
|
|
@ -406,6 +489,7 @@ let parseArgs (argv: string[]) =
|
|||
ServiceSleep = None
|
||||
ServiceWake = None
|
||||
ServiceDestroy = None
|
||||
KeyExtend = false
|
||||
}
|
||||
|
||||
let mutable i = 0
|
||||
|
|
@ -413,6 +497,7 @@ let parseArgs (argv: string[]) =
|
|||
match argv.[i] with
|
||||
| "session" -> args.Command <- Some "session"
|
||||
| "service" -> args.Command <- Some "service"
|
||||
| "key" -> args.Command <- Some "key"
|
||||
| "-k" | "--api-key" -> i <- i + 1; args.ApiKey <- Some argv.[i]
|
||||
| "-n" | "--network" -> i <- i + 1; args.Network <- Some argv.[i]
|
||||
| "-v" | "--vcpu" -> i <- i + 1; args.Vcpu <- int argv.[i]
|
||||
|
|
@ -437,6 +522,7 @@ let parseArgs (argv: string[]) =
|
|||
| "--sleep" -> i <- i + 1; args.ServiceSleep <- Some argv.[i]
|
||||
| "--wake" -> i <- i + 1; args.ServiceWake <- Some argv.[i]
|
||||
| "--destroy" -> i <- i + 1; args.ServiceDestroy <- Some argv.[i]
|
||||
| "--extend" -> args.KeyExtend <- true
|
||||
| arg when not (arg.StartsWith("-")) -> args.SourceFile <- Some arg
|
||||
| _ -> ()
|
||||
i <- i + 1
|
||||
|
|
@ -447,6 +533,7 @@ let printHelp () =
|
|||
printfn "Usage: un [options] <source_file>"
|
||||
printfn " un session [options]"
|
||||
printfn " un service [options]"
|
||||
printfn " un key [options]"
|
||||
printfn ""
|
||||
printfn "Execute options:"
|
||||
printfn " -e KEY=VALUE Set environment variable"
|
||||
|
|
@ -474,6 +561,10 @@ let printHelp () =
|
|||
printfn " --sleep ID Freeze service"
|
||||
printfn " --wake ID Unfreeze service"
|
||||
printfn " --destroy ID Destroy service"
|
||||
printfn ""
|
||||
printfn "Key options:"
|
||||
printfn " --extend Open browser to extend key"
|
||||
printfn " -k KEY API key to validate"
|
||||
|
||||
[<EntryPoint>]
|
||||
let main argv =
|
||||
|
|
@ -483,6 +574,7 @@ let main argv =
|
|||
match args.Command with
|
||||
| Some "session" -> cmdSession args; 0
|
||||
| Some "service" -> cmdService args; 0
|
||||
| Some "key" -> cmdKey args; 0
|
||||
| _ ->
|
||||
match args.SourceFile with
|
||||
| Some _ -> cmdExecute args; 0
|
||||
|
|
|
|||
14
un.go
14
un.go
|
|
@ -63,13 +63,13 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
APIBase = "https://api.unsandbox.com"
|
||||
PortalBase = "https://unsandbox.com"
|
||||
Blue = "\033[34m"
|
||||
Red = "\033[31m"
|
||||
Green = "\033[32m"
|
||||
Yellow = "\033[33m"
|
||||
Reset = "\033[0m"
|
||||
APIBase = "https://api.unsandbox.com"
|
||||
PortalBase = "https://unsandbox.com"
|
||||
Blue = "\033[34m"
|
||||
Red = "\033[31m"
|
||||
Green = "\033[32m"
|
||||
Yellow = "\033[33m"
|
||||
Reset = "\033[0m"
|
||||
)
|
||||
|
||||
var extMap = map[string]string{
|
||||
|
|
|
|||
95
un.groovy
95
un.groovy
|
|
@ -54,6 +54,7 @@ def EXT_MAP = [
|
|||
]
|
||||
|
||||
def API_BASE = 'https://api.unsandbox.com'
|
||||
def PORTAL_BASE = 'https://unsandbox.com'
|
||||
def BLUE = '\033[34m'
|
||||
def RED = '\033[31m'
|
||||
def GREEN = '\033[32m'
|
||||
|
|
@ -84,6 +85,7 @@ class Args {
|
|||
String serviceSleep = null
|
||||
String serviceWake = null
|
||||
String serviceDestroy = null
|
||||
Boolean keyExtend = false
|
||||
}
|
||||
|
||||
def getApiKey(argsKey) {
|
||||
|
|
@ -274,6 +276,86 @@ def cmdSession(args) {
|
|||
println("${YELLOW}(Interactive sessions require WebSocket - use un2 for full support)${RESET}")
|
||||
}
|
||||
|
||||
def openBrowser(url) {
|
||||
def osName = System.getProperty('os.name').toLowerCase()
|
||||
try {
|
||||
if (osName.contains('linux')) {
|
||||
Runtime.runtime.exec(['xdg-open', url] as String[])
|
||||
} else if (osName.contains('mac')) {
|
||||
Runtime.runtime.exec(['open', url] as String[])
|
||||
} else if (osName.contains('win')) {
|
||||
Runtime.runtime.exec(['cmd', '/c', 'start', url] as String[])
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println("${RED}Error opening browser: ${e.message}${RESET}")
|
||||
}
|
||||
}
|
||||
|
||||
def cmdKey(args) {
|
||||
def apiKey = getApiKey(args.apiKey)
|
||||
|
||||
def curlCmd = ['curl', '-s', '-X', 'POST', "${PORTAL_BASE}/keys/validate",
|
||||
'-H', 'Content-Type: application/json',
|
||||
'-H', "Authorization: Bearer ${apiKey}",
|
||||
'-d', '{}']
|
||||
|
||||
def proc = curlCmd.execute()
|
||||
def output = proc.text
|
||||
proc.waitFor()
|
||||
|
||||
if (proc.exitValue() != 0) {
|
||||
println("${RED}Invalid${RESET}")
|
||||
System.err.println("${RED}Error: Failed to validate key${RESET}")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
def publicKeyMatch = output =~ /"public_key":"([^"]+)"/
|
||||
def tierMatch = output =~ /"tier":"([^"]+)"/
|
||||
def statusMatch = output =~ /"status":"([^"]+)"/
|
||||
def expiresAtMatch = output =~ /"expires_at":"([^"]+)"/
|
||||
def timeRemainingMatch = output =~ /"time_remaining":"([^"]+)"/
|
||||
def rateLimitMatch = output =~ /"rate_limit":([0-9.]+)/
|
||||
def burstMatch = output =~ /"burst":([0-9.]+)/
|
||||
def concurrencyMatch = output =~ /"concurrency":([0-9.]+)/
|
||||
def expiredMatch = output =~ /"expired":(true|false)/
|
||||
|
||||
def publicKey = publicKeyMatch.find() ? publicKeyMatch.group(1) : 'N/A'
|
||||
def tier = tierMatch.find() ? tierMatch.group(1) : 'N/A'
|
||||
def status = statusMatch.find() ? statusMatch.group(1) : 'N/A'
|
||||
def expiresAt = expiresAtMatch.find() ? expiresAtMatch.group(1) : 'N/A'
|
||||
def timeRemaining = timeRemainingMatch.find() ? timeRemainingMatch.group(1) : 'N/A'
|
||||
def rateLimit = rateLimitMatch.find() ? rateLimitMatch.group(1) : 'N/A'
|
||||
def burst = burstMatch.find() ? burstMatch.group(1) : 'N/A'
|
||||
def concurrency = concurrencyMatch.find() ? concurrencyMatch.group(1) : 'N/A'
|
||||
def expired = expiredMatch.find() ? expiredMatch.group(1) == 'true' : false
|
||||
|
||||
if (args.keyExtend && publicKey != 'N/A') {
|
||||
def extendUrl = "${PORTAL_BASE}/keys/extend?pk=${publicKey}"
|
||||
println("${BLUE}Opening browser to extend key...${RESET}")
|
||||
openBrowser(extendUrl)
|
||||
return
|
||||
}
|
||||
|
||||
if (expired) {
|
||||
println("${RED}Expired${RESET}")
|
||||
println("Public Key: ${publicKey}")
|
||||
println("Tier: ${tier}")
|
||||
println("Expired: ${expiresAt}")
|
||||
println("${YELLOW}To renew: Visit https://unsandbox.com/keys/extend${RESET}")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
println("${GREEN}Valid${RESET}")
|
||||
println("Public Key: ${publicKey}")
|
||||
println("Tier: ${tier}")
|
||||
println("Status: ${status}")
|
||||
println("Expires: ${expiresAt}")
|
||||
println("Time Remaining: ${timeRemaining}")
|
||||
println("Rate Limit: ${rateLimit}")
|
||||
println("Burst: ${burst}")
|
||||
println("Concurrency: ${concurrency}")
|
||||
}
|
||||
|
||||
def cmdService(args) {
|
||||
def apiKey = getApiKey(args.apiKey)
|
||||
|
||||
|
|
@ -378,6 +460,9 @@ def parseArgs(argv) {
|
|||
case 'service':
|
||||
args.command = 'service'
|
||||
break
|
||||
case 'key':
|
||||
args.command = 'key'
|
||||
break
|
||||
case '-k':
|
||||
case '--api-key':
|
||||
args.apiKey = argv[++i]
|
||||
|
|
@ -448,6 +533,9 @@ def parseArgs(argv) {
|
|||
case '--destroy':
|
||||
args.serviceDestroy = argv[++i]
|
||||
break
|
||||
case '--extend':
|
||||
args.keyExtend = true
|
||||
break
|
||||
default:
|
||||
if (!argv[i].startsWith('-')) {
|
||||
args.sourceFile = argv[i]
|
||||
|
|
@ -462,6 +550,7 @@ def printHelp() {
|
|||
println '''Usage: groovy un.groovy [options] <source_file>
|
||||
groovy un.groovy session [options]
|
||||
groovy un.groovy service [options]
|
||||
groovy un.groovy key [options]
|
||||
|
||||
Execute options:
|
||||
-e KEY=VALUE Set environment variable
|
||||
|
|
@ -489,6 +578,10 @@ Service options:
|
|||
--sleep ID Freeze service
|
||||
--wake ID Unfreeze service
|
||||
--destroy ID Destroy service
|
||||
|
||||
Key options:
|
||||
--extend Open browser to extend key
|
||||
-k KEY API key to validate
|
||||
'''
|
||||
}
|
||||
|
||||
|
|
@ -500,6 +593,8 @@ try {
|
|||
cmdSession(args)
|
||||
} else if (args.command == 'service') {
|
||||
cmdService(args)
|
||||
} else if (args.command == 'key') {
|
||||
cmdKey(args)
|
||||
} else if (args.sourceFile) {
|
||||
cmdExecute(args)
|
||||
} else {
|
||||
|
|
|
|||
19
un.hs
19
un.hs
|
|
@ -69,6 +69,9 @@ import qualified Data.ByteString as BS
|
|||
import qualified Data.ByteString.Base64 as B64
|
||||
|
||||
-- API constants
|
||||
apiBase :: String
|
||||
apiBase = "https://api.unsandbox.com"
|
||||
|
||||
portalBase :: String
|
||||
portalBase = "https://unsandbox.com"
|
||||
|
||||
|
|
@ -431,7 +434,7 @@ keyCommand opts = do
|
|||
validateKey :: String -> IO ()
|
||||
validateKey apiKey = do
|
||||
let url = portalBase ++ "/keys/validate"
|
||||
(exitCode, stdout, stderr) <- curlPost apiKey url "{}"
|
||||
(exitCode, stdout, stderr) <- curlPostPortal apiKey url "{}"
|
||||
|
||||
-- Check if valid:false appears in response
|
||||
let isInvalid = "\"valid\":false" `isPrefixOf` dropWhile (/= 'v') stdout
|
||||
|
|
@ -516,7 +519,7 @@ validateKey apiKey = do
|
|||
extendKey :: String -> IO ()
|
||||
extendKey apiKey = do
|
||||
let url = portalBase ++ "/keys/validate"
|
||||
(exitCode, stdout, _) <- curlPost apiKey url "{}"
|
||||
(exitCode, stdout, _) <- curlPostPortal apiKey url "{}"
|
||||
|
||||
case extractJsonString stdout "public_key" of
|
||||
Nothing -> do
|
||||
|
|
@ -532,3 +535,15 @@ extendKey apiKey = do
|
|||
["-c", "xdg-open '" ++ extendUrl ++ "' 2>/dev/null || sensible-browser '" ++ extendUrl ++ "' 2>/dev/null || true"]
|
||||
""
|
||||
return ()
|
||||
|
||||
-- HTTP helper for portal API
|
||||
curlPostPortal :: String -> String -> String -> IO (ExitCode, String, String)
|
||||
curlPostPortal apiKey url body = do
|
||||
(exitCode, stdout, stderr) <- readProcessWithExitCode "curl"
|
||||
[ "-s", "-X", "POST"
|
||||
, url
|
||||
, "-H", "Content-Type: application/json"
|
||||
, "-H", "Authorization: Bearer " ++ apiKey
|
||||
, "-d", body
|
||||
] ""
|
||||
return (exitCode, stdout, stderr)
|
||||
|
|
|
|||
31
un.js
31
un.js
|
|
@ -224,28 +224,31 @@ async function validateKey(apiKey, shouldExtend = false) {
|
|||
try {
|
||||
const result = await portalRequest("/keys/validate", "POST", {}, apiKey);
|
||||
|
||||
if (result.error || result.status >= 400) {
|
||||
console.log(`${RED}Invalid${RESET}`);
|
||||
console.log(`Reason: ${result.error || 'Unknown error'}`);
|
||||
process.exit(1);
|
||||
// Handle --extend flag first
|
||||
if (shouldExtend) {
|
||||
const public_key = result.public_key;
|
||||
if (public_key) {
|
||||
const extendUrl = `${PORTAL_BASE}/keys/extend?pk=${encodeURIComponent(public_key)}`;
|
||||
console.log(`${BLUE}Opening browser to extend key...${RESET}`);
|
||||
openBrowser(extendUrl);
|
||||
return;
|
||||
} else {
|
||||
console.error(`${RED}Error: Could not retrieve public key${RESET}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if key is expired
|
||||
if (result.expired) {
|
||||
console.log(`${RED}Expired${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || 'N/A'}`);
|
||||
console.log(`Tier: ${result.tier || 'N/A'}`);
|
||||
console.log(`Expired: ${result.expires_at || 'N/A'}`);
|
||||
console.log(`${YELLOW}To renew: Visit https://unsandbox.com/keys/extend${RESET}`);
|
||||
|
||||
if (shouldExtend && result.public_key) {
|
||||
const extendUrl = `${PORTAL_BASE}/keys/extend?pk=${encodeURIComponent(result.public_key)}`;
|
||||
console.log(`\nOpening browser to extend key...`);
|
||||
openBrowser(extendUrl);
|
||||
}
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Valid key
|
||||
console.log(`${GREEN}Valid${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || 'N/A'}`);
|
||||
console.log(`Tier: ${result.tier || 'N/A'}`);
|
||||
|
|
@ -255,12 +258,6 @@ async function validateKey(apiKey, shouldExtend = false) {
|
|||
console.log(`Rate Limit: ${result.rate_limit || 'N/A'}`);
|
||||
console.log(`Burst: ${result.burst || 'N/A'}`);
|
||||
console.log(`Concurrency: ${result.concurrency || 'N/A'}`);
|
||||
|
||||
if (shouldExtend && result.public_key) {
|
||||
const extendUrl = `${PORTAL_BASE}/keys/extend?pk=${encodeURIComponent(result.public_key)}`;
|
||||
console.log(`\nOpening browser to extend key...`);
|
||||
openBrowser(extendUrl);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`${RED}Error validating key: ${error.message}${RESET}`);
|
||||
process.exit(1);
|
||||
|
|
|
|||
8
un.lisp
8
un.lisp
|
|
@ -242,6 +242,14 @@
|
|||
(when public-key (format t "Public Key: ~a~%" public-key))
|
||||
(when tier (format t "Tier: ~a~%" tier))
|
||||
(when expires-at (format t "Expires: ~a~%" expires-at))
|
||||
(let ((time-remaining (parse-json-field response "time_remaining"))
|
||||
(rate-limit (parse-json-field response "rate_limit"))
|
||||
(burst (parse-json-field response "burst"))
|
||||
(concurrency (parse-json-field response "concurrency")))
|
||||
(when time-remaining (format t "Time Remaining: ~a~%" time-remaining))
|
||||
(when rate-limit (format t "Rate Limit: ~a~%" rate-limit))
|
||||
(when burst (format t "Burst: ~a~%" burst))
|
||||
(when concurrency (format t "Concurrency: ~a~%" concurrency)))
|
||||
(when extend-flag
|
||||
(if public-key
|
||||
(let ((extend-url (format nil "~a/keys/extend?pk=~a" *portal-base* public-key)))
|
||||
|
|
|
|||
113
un.m
113
un.m
|
|
@ -43,6 +43,7 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
static NSString* API_BASE = @"https://api.unsandbox.com";
|
||||
static NSString* PORTAL_BASE = @"https://unsandbox.com";
|
||||
static NSString* BLUE = @"\033[34m";
|
||||
static NSString* RED = @"\033[31m";
|
||||
static NSString* GREEN = @"\033[32m";
|
||||
|
|
@ -263,6 +264,115 @@ void cmdExecute(NSArray* args) {
|
|||
exit(exitCode);
|
||||
}
|
||||
|
||||
NSDictionary* portalRequest(NSString* endpoint, NSString* method, NSDictionary* data, NSString* apiKey) {
|
||||
NSString* urlString = [PORTAL_BASE stringByAppendingString:endpoint];
|
||||
NSURL* url = [NSURL URLWithString:urlString];
|
||||
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
|
||||
[request setHTTPMethod:method];
|
||||
[request setValue:[@"Bearer " stringByAppendingString:apiKey] forHTTPHeaderField:@"Authorization"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setTimeoutInterval:30];
|
||||
|
||||
if (data) {
|
||||
NSError* error = nil;
|
||||
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:&error];
|
||||
if (error) {
|
||||
fprintf(stderr, "%sError creating JSON: %s%s\n",
|
||||
[RED UTF8String], [[error localizedDescription] UTF8String], [RESET UTF8String]);
|
||||
exit(1);
|
||||
}
|
||||
[request setHTTPBody:jsonData];
|
||||
}
|
||||
|
||||
NSHTTPURLResponse* response = nil;
|
||||
NSError* error = nil;
|
||||
NSData* responseData = [NSURLConnection sendSynchronousRequest:request
|
||||
returningResponse:&response
|
||||
error:&error];
|
||||
|
||||
if (error || [response statusCode] >= 400) {
|
||||
// For key validation, return the parsed JSON even on error
|
||||
if (responseData) {
|
||||
NSDictionary* result = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "%sError: HTTP %ld%s\n",
|
||||
[RED UTF8String], (long)[response statusCode], [RESET UTF8String]);
|
||||
if (responseData) {
|
||||
NSString* errMsg = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
|
||||
fprintf(stderr, "%s\n", [errMsg UTF8String]);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
NSDictionary* result = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&error];
|
||||
if (error) {
|
||||
fprintf(stderr, "%sError parsing JSON: %s%s\n",
|
||||
[RED UTF8String], [[error localizedDescription] UTF8String], [RESET UTF8String]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void openBrowser(NSString* url) {
|
||||
NSString* command = [NSString stringWithFormat:@"open \"%@\"", url];
|
||||
system([command UTF8String]);
|
||||
}
|
||||
|
||||
void validateKey(NSString* apiKey, BOOL shouldExtend) {
|
||||
NSDictionary* result = portalRequest(@"/keys/validate", @"POST", @{}, apiKey);
|
||||
|
||||
if ([result[@"expired"] boolValue]) {
|
||||
printf("%sExpired%s\n", [RED UTF8String], [RESET UTF8String]);
|
||||
printf("Public Key: %s\n", [result[@"public_key"] UTF8String] ?: "N/A");
|
||||
printf("Tier: %s\n", [result[@"tier"] UTF8String] ?: "N/A");
|
||||
printf("Expired: %s\n", [result[@"expires_at"] UTF8String] ?: "N/A");
|
||||
printf("%sTo renew: Visit https://unsandbox.com/keys/extend%s\n",
|
||||
[YELLOW UTF8String], [RESET UTF8String]);
|
||||
|
||||
if (shouldExtend && result[@"public_key"]) {
|
||||
NSString* extendUrl = [NSString stringWithFormat:@"%@/keys/extend?pk=%@",
|
||||
PORTAL_BASE, result[@"public_key"]];
|
||||
printf("\n%sOpening browser to extend key...%s\n", [BLUE UTF8String], [RESET UTF8String]);
|
||||
openBrowser(extendUrl);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("%sValid%s\n", [GREEN UTF8String], [RESET UTF8String]);
|
||||
printf("Public Key: %s\n", [result[@"public_key"] UTF8String] ?: "N/A");
|
||||
printf("Tier: %s\n", [result[@"tier"] UTF8String] ?: "N/A");
|
||||
printf("Status: %s\n", [result[@"status"] UTF8String] ?: "N/A");
|
||||
printf("Expires: %s\n", [result[@"expires_at"] UTF8String] ?: "N/A");
|
||||
printf("Time Remaining: %s\n", [result[@"time_remaining"] UTF8String] ?: "N/A");
|
||||
printf("Rate Limit: %s\n", [result[@"rate_limit"] UTF8String] ?: "N/A");
|
||||
printf("Burst: %s\n", [result[@"burst"] UTF8String] ?: "N/A");
|
||||
printf("Concurrency: %s\n", [result[@"concurrency"] UTF8String] ?: "N/A");
|
||||
|
||||
if (shouldExtend && result[@"public_key"]) {
|
||||
NSString* extendUrl = [NSString stringWithFormat:@"%@/keys/extend?pk=%@",
|
||||
PORTAL_BASE, result[@"public_key"]];
|
||||
printf("\n%sOpening browser to extend key...%s\n", [BLUE UTF8String], [RESET UTF8String]);
|
||||
openBrowser(extendUrl);
|
||||
}
|
||||
}
|
||||
|
||||
void cmdKey(NSArray* args) {
|
||||
NSString* apiKey = getApiKey();
|
||||
BOOL shouldExtend = NO;
|
||||
|
||||
for (NSString* arg in args) {
|
||||
if ([arg isEqualToString:@"--extend"]) {
|
||||
shouldExtend = YES;
|
||||
}
|
||||
}
|
||||
|
||||
validateKey(apiKey, shouldExtend);
|
||||
}
|
||||
|
||||
void cmdSession(NSArray* args) {
|
||||
NSString* apiKey = getApiKey();
|
||||
BOOL listMode = NO;
|
||||
|
|
@ -481,6 +591,7 @@ int main(int argc, const char* argv[]) {
|
|||
fprintf(stderr, "Usage: un.m [options] <source_file>\n");
|
||||
fprintf(stderr, " un.m session [options]\n");
|
||||
fprintf(stderr, " un.m service [options]\n");
|
||||
fprintf(stderr, " un.m key [options]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -495,6 +606,8 @@ int main(int argc, const char* argv[]) {
|
|||
cmdSession([args subarrayWithRange:NSMakeRange(1, [args count] - 1)]);
|
||||
} else if ([firstArg isEqualToString:@"service"]) {
|
||||
cmdService([args subarrayWithRange:NSMakeRange(1, [args count] - 1)]);
|
||||
} else if ([firstArg isEqualToString:@"key"]) {
|
||||
cmdKey([args subarrayWithRange:NSMakeRange(1, [args count] - 1)]);
|
||||
} else {
|
||||
cmdExecute(args);
|
||||
}
|
||||
|
|
|
|||
30
un.ml
30
un.ml
|
|
@ -276,24 +276,34 @@ let display_key_info response extend =
|
|||
let status = extract_json_value response "status" in
|
||||
let public_key = extract_json_value response "public_key" in
|
||||
let tier = extract_json_value response "tier" in
|
||||
let expires_at = extract_json_value response "expires_at" in
|
||||
let valid_through = extract_json_value response "valid_through_datetime" in
|
||||
let valid_for = extract_json_value response "valid_for_human" in
|
||||
let rate_limit = extract_json_value response "rate_per_minute" in
|
||||
let burst = extract_json_value response "burst" in
|
||||
let concurrency = extract_json_value response "concurrency" in
|
||||
let expired_at = extract_json_value response "expired_at_datetime" in
|
||||
|
||||
match status with
|
||||
| Some "valid" ->
|
||||
Printf.printf "%sValid%s\n" green reset;
|
||||
(match public_key with Some pk -> Printf.printf "Public Key: %s\n" pk | None -> ());
|
||||
(match tier with Some t -> Printf.printf "Tier: %s\n" t | None -> ());
|
||||
(match expires_at with Some exp -> Printf.printf "Expires: %s\n" exp | None -> ());
|
||||
Printf.printf "%sValid%s\n\n" green reset;
|
||||
(match public_key with Some pk -> Printf.printf "Public Key: %s\n" pk | None -> ());
|
||||
(match tier with Some t -> Printf.printf "Tier: %s\n" t | None -> ());
|
||||
Printf.printf "Status: valid\n";
|
||||
(match valid_through with Some exp -> Printf.printf "Expires: %s\n" exp | None -> ());
|
||||
(match valid_for with Some vf -> Printf.printf "Time Remaining: %s\n" vf | None -> ());
|
||||
(match rate_limit with Some r -> Printf.printf "Rate Limit: %s/min\n" r | None -> ());
|
||||
(match burst with Some b -> Printf.printf "Burst: %s\n" b | None -> ());
|
||||
(match concurrency with Some c -> Printf.printf "Concurrency: %s\n" c | None -> ());
|
||||
if extend then
|
||||
(match public_key with
|
||||
| Some pk -> open_browser (Printf.sprintf "%s/keys/extend?pk=%s" portal_base pk)
|
||||
| None -> ())
|
||||
| Some "expired" ->
|
||||
Printf.printf "%sExpired%s\n" red reset;
|
||||
(match public_key with Some pk -> Printf.printf "Public Key: %s\n" pk | None -> ());
|
||||
(match tier with Some t -> Printf.printf "Tier: %s\n" t | None -> ());
|
||||
(match expires_at with Some exp -> Printf.printf "Expired: %s\n" exp | None -> ());
|
||||
Printf.printf "%sTo renew: Visit %s/keys/extend%s\n" yellow portal_base reset;
|
||||
Printf.printf "%sExpired%s\n\n" red reset;
|
||||
(match public_key with Some pk -> Printf.printf "Public Key: %s\n" pk | None -> ());
|
||||
(match tier with Some t -> Printf.printf "Tier: %s\n" t | None -> ());
|
||||
(match expired_at with Some exp -> Printf.printf "Expired: %s\n" exp | None -> ());
|
||||
Printf.printf "\n%sTo renew:%s Visit %s/keys/extend\n" yellow reset portal_base;
|
||||
if extend then
|
||||
(match public_key with
|
||||
| Some pk -> open_browser (Printf.sprintf "%s/keys/extend?pk=%s" portal_base pk)
|
||||
|
|
|
|||
84
un.pl
84
un.pl
|
|
@ -362,9 +362,22 @@ sub cmd_service {
|
|||
exit 1;
|
||||
}
|
||||
|
||||
sub cmd_key {
|
||||
my ($options) = @_;
|
||||
my $api_key = get_api_key($options->{api_key});
|
||||
sub open_browser {
|
||||
my ($url) = @_;
|
||||
|
||||
# Try different browser open commands based on platform
|
||||
if ($^O eq 'darwin') {
|
||||
system('open', $url);
|
||||
} elsif ($^O eq 'MSWin32') {
|
||||
system('start', $url);
|
||||
} else {
|
||||
# Linux/Unix
|
||||
system('xdg-open', $url, '>/dev/null', '2>&1', '&');
|
||||
}
|
||||
}
|
||||
|
||||
sub validate_key {
|
||||
my ($api_key, $should_extend) = @_;
|
||||
|
||||
# Call /keys/validate endpoint
|
||||
my $url = "$PORTAL_BASE/keys/validate";
|
||||
|
|
@ -374,47 +387,48 @@ sub cmd_key {
|
|||
$request->header('Content-Type' => 'application/json');
|
||||
|
||||
my $response = $ua->request($request);
|
||||
|
||||
unless ($response->is_success) {
|
||||
print STDERR "${RED}Error: HTTP ", $response->code, " - ", $response->content, "${RESET}\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my $result = decode_json($response->content);
|
||||
|
||||
# Handle different states
|
||||
my $status = $result->{status} || 'unknown';
|
||||
# Handle --extend flag first
|
||||
if ($should_extend) {
|
||||
my $public_key = $result->{public_key};
|
||||
if ($public_key) {
|
||||
my $extend_url = "$PORTAL_BASE/keys/extend?pk=$public_key";
|
||||
print "${BLUE}Opening browser to extend key...${RESET}\n";
|
||||
open_browser($extend_url);
|
||||
return;
|
||||
} else {
|
||||
print STDERR "${RED}Error: Could not retrieve public key${RESET}\n";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
if ($status eq 'valid') {
|
||||
print "${GREEN}Valid${RESET}\n";
|
||||
print "Public Key: ", ($result->{public_key} // 'N/A'), "\n";
|
||||
print "Tier: ", ($result->{tier} // 'N/A'), "\n";
|
||||
print "Expires: ", ($result->{expires_at} // 'N/A'), "\n";
|
||||
} elsif ($status eq 'expired') {
|
||||
# Check if key is expired
|
||||
if ($result->{expired}) {
|
||||
print "${RED}Expired${RESET}\n";
|
||||
print "Public Key: ", ($result->{public_key} // 'N/A'), "\n";
|
||||
print "Tier: ", ($result->{tier} // 'N/A'), "\n";
|
||||
print "Expired: ", ($result->{expired_at} // 'N/A'), "\n";
|
||||
print "Expired: ", ($result->{expires_at} // 'N/A'), "\n";
|
||||
print "${YELLOW}To renew: Visit https://unsandbox.com/keys/extend${RESET}\n";
|
||||
|
||||
# Handle --extend flag for expired keys
|
||||
if ($options->{extend} && $result->{public_key}) {
|
||||
my $extend_url = "$PORTAL_BASE/keys/extend?pk=$result->{public_key}";
|
||||
print "\n${BLUE}Opening browser to: $extend_url${RESET}\n";
|
||||
system("xdg-open", $extend_url) if -x "/usr/bin/xdg-open";
|
||||
}
|
||||
} elsif ($status eq 'invalid') {
|
||||
print "${RED}Invalid${RESET}\n";
|
||||
} else {
|
||||
print "${YELLOW}Unknown status: $status${RESET}\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Handle --extend flag for valid keys
|
||||
if ($options->{extend} && $status eq 'valid' && $result->{public_key}) {
|
||||
my $extend_url = "$PORTAL_BASE/keys/extend?pk=$result->{public_key}";
|
||||
print "\n${BLUE}Opening browser to: $extend_url${RESET}\n";
|
||||
system("xdg-open", $extend_url) if -x "/usr/bin/xdg-open";
|
||||
}
|
||||
# Valid key
|
||||
print "${GREEN}Valid${RESET}\n";
|
||||
print "Public Key: ", ($result->{public_key} // 'N/A'), "\n";
|
||||
print "Tier: ", ($result->{tier} // 'N/A'), "\n";
|
||||
print "Status: ", ($result->{status} // 'N/A'), "\n";
|
||||
print "Expires: ", ($result->{expires_at} // 'N/A'), "\n";
|
||||
print "Time Remaining: ", ($result->{time_remaining} // 'N/A'), "\n";
|
||||
print "Rate Limit: ", ($result->{rate_limit} // 'N/A'), "\n";
|
||||
print "Burst: ", ($result->{burst} // 'N/A'), "\n";
|
||||
print "Concurrency: ", ($result->{concurrency} // 'N/A'), "\n";
|
||||
}
|
||||
|
||||
sub cmd_key {
|
||||
my ($options) = @_;
|
||||
my $api_key = get_api_key($options->{api_key});
|
||||
validate_key($api_key, $options->{extend});
|
||||
}
|
||||
|
||||
sub main {
|
||||
|
|
|
|||
25
un.pro
25
un.pro
|
|
@ -39,6 +39,9 @@
|
|||
|
||||
:- initialization(main, main).
|
||||
|
||||
% Constants
|
||||
portal_base('https://unsandbox.com').
|
||||
|
||||
% Extension to language mapping
|
||||
ext_lang('.jl', 'julia').
|
||||
ext_lang('.r', 'r').
|
||||
|
|
@ -198,6 +201,26 @@ service_create(Name, Ports, Bootstrap, ServiceType) :-
|
|||
[ApiKey, Json]),
|
||||
shell(Cmd, 0).
|
||||
|
||||
% Key validate
|
||||
validate_key(Extend) :-
|
||||
get_api_key(ApiKey),
|
||||
portal_base(PortalBase),
|
||||
( Extend = true
|
||||
-> % Build command for --extend mode
|
||||
format(atom(Cmd),
|
||||
'RESP=$(curl -s -X POST ~w/keys/validate -H "Content-Type: application/json" -H "Authorization: Bearer ~w"); PUBLIC_KEY=$(echo "$RESP" | jq -r ".public_key // \\"N/A\\""); xdg-open "~w/keys/extend?pk=$PUBLIC_KEY" 2>/dev/null',
|
||||
[PortalBase, ApiKey, PortalBase])
|
||||
; % Build command for normal validation
|
||||
format(atom(Cmd),
|
||||
'curl -s -X POST ~w/keys/validate -H "Content-Type: application/json" -H "Authorization: Bearer ~w" -o /tmp/unsandbox_key_resp.json; STATUS=$?; if [ $STATUS -ne 0 ]; then echo -e "\\x1b[31mInvalid\\x1b[0m"; exit 1; fi; EXPIRED=$(jq -r ".expired // false" /tmp/unsandbox_key_resp.json); if [ "$EXPIRED" = "true" ]; then echo -e "\\x1b[31mExpired\\x1b[0m"; echo "Public Key: $(jq -r ".public_key // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Tier: $(jq -r ".tier // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Expired: $(jq -r ".expires_at // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo -e "\\x1b[33mTo renew: Visit https://unsandbox.com/keys/extend\\x1b[0m"; rm -f /tmp/unsandbox_key_resp.json; exit 1; else echo -e "\\x1b[32mValid\\x1b[0m"; echo "Public Key: $(jq -r ".public_key // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Tier: $(jq -r ".tier // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Status: $(jq -r ".status // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Expires: $(jq -r ".expires_at // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Time Remaining: $(jq -r ".time_remaining // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Rate Limit: $(jq -r ".rate_limit // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Burst: $(jq -r ".burst // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; echo "Concurrency: $(jq -r ".concurrency // \\"N/A\\"" /tmp/unsandbox_key_resp.json)"; fi; rm -f /tmp/unsandbox_key_resp.json',
|
||||
[PortalBase, ApiKey])
|
||||
),
|
||||
shell(Cmd, 0).
|
||||
|
||||
% Handle key subcommand
|
||||
handle_key(['--extend'|_]) :- validate_key(true).
|
||||
handle_key(_) :- validate_key(false).
|
||||
|
||||
% Handle session subcommand
|
||||
handle_session(['--list'|_]) :- session_list.
|
||||
handle_session(['-l'|_]) :- session_list.
|
||||
|
|
@ -261,6 +284,8 @@ main(Argv) :-
|
|||
-> handle_session(Rest)
|
||||
; Argv = ['service'|Rest]
|
||||
-> handle_service(Rest)
|
||||
; Argv = ['key'|Rest]
|
||||
-> handle_key(Rest)
|
||||
; Argv = [Filename|_]
|
||||
-> execute_file(Filename)
|
||||
; write(user_error, 'Error: Invalid arguments\n'),
|
||||
|
|
|
|||
69
un.ps1
69
un.ps1
|
|
@ -43,6 +43,7 @@
|
|||
# pwsh un.ps1 service [options]
|
||||
|
||||
$API_BASE = "https://api.unsandbox.com"
|
||||
$PORTAL_BASE = "https://unsandbox.com"
|
||||
|
||||
$EXT_MAP = @{
|
||||
".ps1" = "powershell"; ".py" = "python"; ".js" = "javascript"
|
||||
|
|
@ -68,7 +69,7 @@ function Get-ApiKey {
|
|||
}
|
||||
|
||||
function Invoke-Api {
|
||||
param($Endpoint, $Method = "GET", $Body = $null)
|
||||
param($Endpoint, $Method = "GET", $Body = $null, $BaseUrl = $null)
|
||||
|
||||
$apiKey = Get-ApiKey
|
||||
$headers = @{
|
||||
|
|
@ -76,7 +77,8 @@ function Invoke-Api {
|
|||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
$uri = "$API_BASE$Endpoint"
|
||||
$base = if ($BaseUrl) { $BaseUrl } else { $API_BASE }
|
||||
$uri = "$base$Endpoint"
|
||||
|
||||
try {
|
||||
if ($Body) {
|
||||
|
|
@ -164,6 +166,63 @@ function Invoke-Session {
|
|||
$result | ConvertTo-Json -Depth 5
|
||||
}
|
||||
|
||||
function Invoke-Key {
|
||||
param($Args)
|
||||
|
||||
$extend = $Args -contains "--extend"
|
||||
|
||||
try {
|
||||
$result = Invoke-Api -Endpoint "/keys/validate" -Method "POST" -BaseUrl $PORTAL_BASE
|
||||
|
||||
# Handle --extend flag
|
||||
if ($extend) {
|
||||
$publicKey = $result.public_key
|
||||
if ($publicKey) {
|
||||
$url = "$PORTAL_BASE/keys/extend?pk=$publicKey"
|
||||
Write-Host "`e[34mOpening browser to extend key...`e[0m"
|
||||
if ($IsWindows) {
|
||||
Start-Process $url
|
||||
} elseif ($IsMacOS) {
|
||||
& open $url
|
||||
} elseif ($IsLinux) {
|
||||
& xdg-open $url
|
||||
} else {
|
||||
Write-Host "`e[33mPlease open manually: $url`e[0m"
|
||||
}
|
||||
return
|
||||
} else {
|
||||
Write-Error "Error: Could not retrieve public key"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Check if key is expired
|
||||
if ($result.expired) {
|
||||
Write-Host "`e[31mExpired`e[0m"
|
||||
Write-Host "Public Key: $($result.public_key ?? 'N/A')"
|
||||
Write-Host "Tier: $($result.tier ?? 'N/A')"
|
||||
Write-Host "Expired: $($result.expires_at ?? 'N/A')"
|
||||
Write-Host "`e[33mTo renew: Visit $PORTAL_BASE/keys/extend`e[0m"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Valid key
|
||||
Write-Host "`e[32mValid`e[0m"
|
||||
Write-Host "Public Key: $($result.public_key ?? 'N/A')"
|
||||
Write-Host "Tier: $($result.tier ?? 'N/A')"
|
||||
Write-Host "Status: $($result.status ?? 'N/A')"
|
||||
Write-Host "Expires: $($result.expires_at ?? 'N/A')"
|
||||
Write-Host "Time Remaining: $($result.time_remaining ?? 'N/A')"
|
||||
Write-Host "Rate Limit: $($result.rate_limit ?? 'N/A')"
|
||||
Write-Host "Burst: $($result.burst ?? 'N/A')"
|
||||
Write-Host "Concurrency: $($result.concurrency ?? 'N/A')"
|
||||
} catch {
|
||||
Write-Host "`e[31mInvalid`e[0m"
|
||||
Write-Host "Reason: $($_.Exception.Message)"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-Service {
|
||||
param($Args)
|
||||
|
||||
|
|
@ -253,6 +312,7 @@ if ($args.Count -eq 0 -or $args[0] -eq "--help" -or $args[0] -eq "-h") {
|
|||
Usage: pwsh un.ps1 [options] <source_file>
|
||||
pwsh un.ps1 session [options]
|
||||
pwsh un.ps1 service [options]
|
||||
pwsh un.ps1 key [options]
|
||||
|
||||
Execute options:
|
||||
-e KEY=VALUE Environment variable
|
||||
|
|
@ -274,6 +334,9 @@ Service options:
|
|||
--sleep ID Freeze service
|
||||
--wake ID Unfreeze service
|
||||
--destroy ID Destroy service
|
||||
|
||||
Key options:
|
||||
--extend Open browser to extend key
|
||||
"@
|
||||
exit 0
|
||||
}
|
||||
|
|
@ -282,6 +345,8 @@ if ($args[0] -eq "session") {
|
|||
Invoke-Session -Args $args[1..($args.Count-1)]
|
||||
} elseif ($args[0] -eq "service") {
|
||||
Invoke-Service -Args $args[1..($args.Count-1)]
|
||||
} elseif ($args[0] -eq "key") {
|
||||
Invoke-Key -Args $args[1..($args.Count-1)]
|
||||
} else {
|
||||
# Parse execute args
|
||||
$sourceFile = $null
|
||||
|
|
|
|||
74
un.raku
74
un.raku
|
|
@ -42,6 +42,7 @@
|
|||
use JSON::Fast;
|
||||
|
||||
constant $API_BASE = "https://api.unsandbox.com";
|
||||
constant $PORTAL_BASE = "https://unsandbox.com";
|
||||
constant $BLUE = "\e[34m";
|
||||
constant $RED = "\e[31m";
|
||||
constant $GREEN = "\e[32m";
|
||||
|
|
@ -462,11 +463,81 @@ sub cmd-service(@args) {
|
|||
exit 1;
|
||||
}
|
||||
|
||||
sub validate-key(Bool $extend) {
|
||||
my $api-key = get-api-key();
|
||||
|
||||
# Build curl command
|
||||
my @args = 'curl', '-s', '-X', 'POST';
|
||||
@args.append: "$PORTAL_BASE/keys/validate";
|
||||
@args.append: '-H', 'Content-Type: application/json';
|
||||
@args.append: '-H', "Authorization: Bearer $api-key";
|
||||
|
||||
my $proc = run |@args, :out, :err;
|
||||
my $body = $proc.out.slurp;
|
||||
my $err-msg = $proc.err.slurp;
|
||||
|
||||
if $proc.exitcode != 0 {
|
||||
say "{$RED}Invalid{$RESET}";
|
||||
note "Reason: $err-msg" if $err-msg;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
my %result = from-json($body);
|
||||
|
||||
# Handle --extend flag
|
||||
if $extend {
|
||||
my $public-key = %result<public_key>;
|
||||
if $public-key {
|
||||
say "{$BLUE}Opening browser to extend key...{$RESET}";
|
||||
run 'xdg-open', "$PORTAL_BASE/keys/extend?pk=$public-key";
|
||||
return;
|
||||
} else {
|
||||
note "{$RED}Error: Could not retrieve public key{$RESET}";
|
||||
exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Check if key is expired
|
||||
if %result<expired> {
|
||||
say "{$RED}Expired{$RESET}";
|
||||
say "Public Key: {%result<public_key> // 'N/A'}";
|
||||
say "Tier: {%result<tier> // 'N/A'}";
|
||||
say "Expired: {%result<expires_at> // 'N/A'}";
|
||||
say "{$YELLOW}To renew: Visit https://unsandbox.com/keys/extend{$RESET}";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# Valid key
|
||||
say "{$GREEN}Valid{$RESET}";
|
||||
say "Public Key: {%result<public_key> // 'N/A'}";
|
||||
say "Tier: {%result<tier> // 'N/A'}";
|
||||
say "Status: {%result<status> // 'N/A'}";
|
||||
say "Expires: {%result<expires_at> // 'N/A'}";
|
||||
say "Time Remaining: {%result<time_remaining> // 'N/A'}";
|
||||
say "Rate Limit: {%result<rate_limit> // 'N/A'}";
|
||||
say "Burst: {%result<burst> // 'N/A'}";
|
||||
say "Concurrency: {%result<concurrency> // 'N/A'}";
|
||||
}
|
||||
|
||||
sub cmd-key(@args) {
|
||||
my $extend = False;
|
||||
|
||||
# Parse arguments
|
||||
for @args -> $arg {
|
||||
if $arg eq '--extend' {
|
||||
$extend = True;
|
||||
}
|
||||
}
|
||||
|
||||
validate-key($extend);
|
||||
}
|
||||
|
||||
sub MAIN(*@args) {
|
||||
unless @args {
|
||||
note "Usage: un.raku [options] <source_file>";
|
||||
note " un.raku session [options]";
|
||||
note " un.raku service [options]";
|
||||
note " un.raku key [options]";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
|
@ -477,6 +548,9 @@ sub MAIN(*@args) {
|
|||
when 'service' {
|
||||
cmd-service(@args[1..*]);
|
||||
}
|
||||
when 'key' {
|
||||
cmd-key(@args[1..*]);
|
||||
}
|
||||
default {
|
||||
cmd-execute(@args);
|
||||
}
|
||||
|
|
|
|||
34
un.scm
34
un.scm
|
|
@ -181,33 +181,43 @@
|
|||
(status (json-extract-string response "status"))
|
||||
(public-key (json-extract-string response "public_key"))
|
||||
(tier (json-extract-string response "tier"))
|
||||
(expires-at (json-extract-string response "expires_at")))
|
||||
(valid-through (json-extract-string response "valid_through_datetime"))
|
||||
(valid-for (json-extract-string response "valid_for_human"))
|
||||
(rate-limit (json-extract-string response "rate_per_minute"))
|
||||
(burst (json-extract-string response "burst"))
|
||||
(concurrency (json-extract-string response "concurrency"))
|
||||
(expired-at (json-extract-string response "expired_at_datetime")))
|
||||
|
||||
(cond
|
||||
;; Valid key
|
||||
((and status (string=? status "valid"))
|
||||
(format #t "~aValid~a\n" green reset)
|
||||
(when public-key (format #t "Public Key: ~a\n" public-key))
|
||||
(when tier (format #t "Tier: ~a\n" tier))
|
||||
(when expires-at (format #t "Expires: ~a\n" expires-at))
|
||||
(format #t "~aValid~a\n\n" green reset)
|
||||
(when public-key (format #t "Public Key: ~a\n" public-key))
|
||||
(when tier (format #t "Tier: ~a\n" tier))
|
||||
(format #t "Status: valid\n")
|
||||
(when valid-through (format #t "Expires: ~a\n" valid-through))
|
||||
(when valid-for (format #t "Time Remaining: ~a\n" valid-for))
|
||||
(when rate-limit (format #t "Rate Limit: ~a/min\n" rate-limit))
|
||||
(when burst (format #t "Burst: ~a\n" burst))
|
||||
(when concurrency (format #t "Concurrency: ~a\n" concurrency))
|
||||
(when extend
|
||||
(if public-key
|
||||
(let ((url (format #f "~a/keys/extend?pk=~a" portal-base public-key)))
|
||||
(format #t "~aOpening browser to extend key...~a\n" yellow reset)
|
||||
(format #t "~aOpening browser to extend key...~a\n" blue reset)
|
||||
(open-browser url))
|
||||
(format #t "~aError: No public_key in response~a\n" red reset))))
|
||||
|
||||
;; Expired key
|
||||
((and status (string=? status "expired"))
|
||||
(format #t "~aExpired~a\n" red reset)
|
||||
(when public-key (format #t "Public Key: ~a\n" public-key))
|
||||
(when tier (format #t "Tier: ~a\n" tier))
|
||||
(when expires-at (format #t "Expired: ~a\n" expires-at))
|
||||
(format #t "~aTo renew: Visit ~a/keys/extend~a\n" yellow portal-base reset)
|
||||
(format #t "~aExpired~a\n\n" red reset)
|
||||
(when public-key (format #t "Public Key: ~a\n" public-key))
|
||||
(when tier (format #t "Tier: ~a\n" tier))
|
||||
(when expired-at (format #t "Expired: ~a\n" expired-at))
|
||||
(format #t "\n~aTo renew:~a Visit ~a/keys/extend\n" yellow reset portal-base)
|
||||
(when extend
|
||||
(if public-key
|
||||
(let ((url (format #f "~a/keys/extend?pk=~a" portal-base public-key)))
|
||||
(format #t "~aOpening browser...~a\n" yellow reset)
|
||||
(format #t "~aOpening browser...~a\n" blue reset)
|
||||
(open-browser url))
|
||||
(format #t "~aError: No public_key in response~a\n" red reset))))
|
||||
|
||||
|
|
|
|||
126
un.ts
126
un.ts
|
|
@ -186,6 +186,44 @@ function apiRequest(endpoint: string, method: string = "GET", data: any = null,
|
|||
});
|
||||
}
|
||||
|
||||
function portalRequest(endpoint: string, method: string = "GET", data: any = null, apiKey: string): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const url = new URL(PORTAL_BASE + endpoint);
|
||||
const options: https.RequestOptions = {
|
||||
hostname: url.hostname,
|
||||
path: url.pathname + url.search,
|
||||
method: method,
|
||||
headers: {
|
||||
'Authorization': `Bearer ${apiKey}`,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
timeout: 30000
|
||||
};
|
||||
|
||||
const req = https.request(options, (res) => {
|
||||
let body = '';
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => {
|
||||
try {
|
||||
const parsed = JSON.parse(body);
|
||||
resolve(parsed);
|
||||
} catch (e) {
|
||||
resolve({ error: body, status: res.statusCode });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
req.on('error', (e) => {
|
||||
reject(e);
|
||||
});
|
||||
|
||||
if (data) {
|
||||
req.write(JSON.stringify(data));
|
||||
}
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function cmdExecute(args: Args): Promise<void> {
|
||||
const apiKey = getApiKey(args.apiKey);
|
||||
|
||||
|
|
@ -381,42 +419,76 @@ async function cmdService(args: Args): Promise<void> {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
async function cmdKey(args: Args): Promise<void> {
|
||||
const apiKey = getApiKey(args.apiKey);
|
||||
function openBrowser(url: string): void {
|
||||
const { exec } = require('child_process');
|
||||
const platform = process.platform;
|
||||
let command: string;
|
||||
|
||||
// Validate the key
|
||||
const result = await apiRequest("/keys/validate", "POST", {}, apiKey);
|
||||
if (platform === 'darwin') {
|
||||
command = `open "${url}"`;
|
||||
} else if (platform === 'win32') {
|
||||
command = `start "${url}"`;
|
||||
} else {
|
||||
command = `xdg-open "${url}"`;
|
||||
}
|
||||
|
||||
if (result.status === "valid") {
|
||||
exec(command, (error: any) => {
|
||||
if (error) {
|
||||
console.error(`${RED}Error opening browser: ${error.message}${RESET}`);
|
||||
console.log(`Please visit: ${url}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function validateKey(apiKey: string, shouldExtend: boolean): Promise<void> {
|
||||
try {
|
||||
const result = await portalRequest("/keys/validate", "POST", {}, apiKey);
|
||||
|
||||
// Handle --extend flag first
|
||||
if (shouldExtend) {
|
||||
const public_key = result.public_key;
|
||||
if (public_key) {
|
||||
const extendUrl = `${PORTAL_BASE}/keys/extend?pk=${encodeURIComponent(public_key)}`;
|
||||
console.log(`${BLUE}Opening browser to extend key...${RESET}`);
|
||||
openBrowser(extendUrl);
|
||||
return;
|
||||
} else {
|
||||
console.error(`${RED}Error: Could not retrieve public key${RESET}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if key is expired
|
||||
if (result.expired) {
|
||||
console.log(`${RED}Expired${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || 'N/A'}`);
|
||||
console.log(`Tier: ${result.tier || 'N/A'}`);
|
||||
console.log(`Expired: ${result.expires_at || 'N/A'}`);
|
||||
console.log(`${YELLOW}To renew: Visit https://unsandbox.com/keys/extend${RESET}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Valid key
|
||||
console.log(`${GREEN}Valid${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || 'N/A'}`);
|
||||
console.log(`Tier: ${result.tier || 'N/A'}`);
|
||||
console.log(`Status: ${result.status || 'N/A'}`);
|
||||
console.log(`Expires: ${result.expires_at || 'N/A'}`);
|
||||
|
||||
// Handle --extend flag
|
||||
if (args.extend && result.public_key) {
|
||||
const extendUrl = `${PORTAL_BASE}/keys/extend?pk=${result.public_key}`;
|
||||
console.log(`\n${BLUE}Opening: ${extendUrl}${RESET}`);
|
||||
|
||||
// Try to open browser using common commands
|
||||
const { exec } = require('child_process');
|
||||
exec(`xdg-open "${extendUrl}" || open "${extendUrl}" || start "${extendUrl}"`, (error: any) => {
|
||||
if (error) {
|
||||
console.error(`${YELLOW}Could not open browser automatically. Visit: ${extendUrl}${RESET}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (result.status === "expired") {
|
||||
console.log(`${RED}Expired${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || 'N/A'}`);
|
||||
console.log(`Tier: ${result.tier || 'N/A'}`);
|
||||
console.log(`Expired: ${result.expires_at || 'N/A'}`);
|
||||
console.log(`${YELLOW}To renew: Visit ${PORTAL_BASE}/keys/extend${RESET}`);
|
||||
} else {
|
||||
console.log(`${RED}Invalid${RESET}`);
|
||||
console.log(`Time Remaining: ${result.time_remaining || 'N/A'}`);
|
||||
console.log(`Rate Limit: ${result.rate_limit || 'N/A'}`);
|
||||
console.log(`Burst: ${result.burst || 'N/A'}`);
|
||||
console.log(`Concurrency: ${result.concurrency || 'N/A'}`);
|
||||
} catch (error: any) {
|
||||
console.error(`${RED}Error validating key: ${error.message}${RESET}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdKey(args: Args): Promise<void> {
|
||||
const apiKey = getApiKey(args.apiKey);
|
||||
await validateKey(apiKey, args.extend);
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): Args {
|
||||
const args: Args = {
|
||||
command: null,
|
||||
|
|
|
|||
69
un.v
69
un.v
|
|
@ -126,17 +126,22 @@ fn extract_json_string(json string, key string) string {
|
|||
}
|
||||
|
||||
fn cmd_key(extend bool, api_key string) {
|
||||
cmd := "curl -s -X POST '${api_base}/keys/validate' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${api_key}' -d '{}'"
|
||||
cmd := "curl -s -X POST '${portal_base}/keys/validate' -H 'Content-Type: application/json' -H 'Authorization: Bearer ${api_key}' -d '{}'"
|
||||
result := exec_curl(cmd)
|
||||
|
||||
status := extract_json_string(result, 'status')
|
||||
public_key := extract_json_string(result, 'public_key')
|
||||
tier := extract_json_string(result, 'tier')
|
||||
expired_at := extract_json_string(result, 'expired_at')
|
||||
status := extract_json_string(result, 'status')
|
||||
expires_at := extract_json_string(result, 'expires_at')
|
||||
time_remaining := extract_json_string(result, 'time_remaining')
|
||||
rate_limit := extract_json_string(result, 'rate_limit')
|
||||
burst := extract_json_string(result, 'burst')
|
||||
concurrency := extract_json_string(result, 'concurrency')
|
||||
expired := extract_json_string(result, 'expired')
|
||||
|
||||
if extend && public_key != '' {
|
||||
url := '${portal_base}/keys/extend?pk=${public_key}'
|
||||
println('${yellow}Opening browser: ${url}${reset}')
|
||||
println('${blue}Opening browser to extend key...${reset}')
|
||||
|
||||
// Try xdg-open (Linux), open (macOS), or start (Windows)
|
||||
os.execute('xdg-open "${url}"') or {
|
||||
|
|
@ -149,30 +154,40 @@ fn cmd_key(extend bool, api_key string) {
|
|||
return
|
||||
}
|
||||
|
||||
match status {
|
||||
'valid' {
|
||||
println('${green}Valid${reset}')
|
||||
println('Public Key: ${public_key}')
|
||||
println('Tier: ${tier}')
|
||||
if expired_at != '' {
|
||||
println('Expires: ${expired_at}')
|
||||
}
|
||||
}
|
||||
'expired' {
|
||||
println('${red}Expired${reset}')
|
||||
println('Public Key: ${public_key}')
|
||||
println('Tier: ${tier}')
|
||||
if expired_at != '' {
|
||||
println('Expired: ${expired_at}')
|
||||
}
|
||||
println('${yellow}To renew: Visit ${portal_base}/keys/extend${reset}')
|
||||
}
|
||||
'invalid' {
|
||||
println('${red}Invalid${reset}')
|
||||
}
|
||||
else {
|
||||
println('${yellow}Unknown status: ${status}${reset}')
|
||||
if expired == 'true' {
|
||||
println('${red}Expired${reset}')
|
||||
println('Public Key: ${public_key}')
|
||||
println('Tier: ${tier}')
|
||||
if expires_at != '' {
|
||||
println('Expired: ${expires_at}')
|
||||
}
|
||||
println('${yellow}To renew: Visit https://unsandbox.com/keys/extend${reset}')
|
||||
exit(1)
|
||||
}
|
||||
|
||||
// Valid key
|
||||
println('${green}Valid${reset}')
|
||||
println('Public Key: ${public_key}')
|
||||
if tier != '' {
|
||||
println('Tier: ${tier}')
|
||||
}
|
||||
if status != '' {
|
||||
println('Status: ${status}')
|
||||
}
|
||||
if expires_at != '' {
|
||||
println('Expires: ${expires_at}')
|
||||
}
|
||||
if time_remaining != '' {
|
||||
println('Time Remaining: ${time_remaining}')
|
||||
}
|
||||
if rate_limit != '' {
|
||||
println('Rate Limit: ${rate_limit}')
|
||||
}
|
||||
if burst != '' {
|
||||
println('Burst: ${burst}')
|
||||
}
|
||||
if concurrency != '' {
|
||||
println('Concurrency: ${concurrency}')
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
71
un_deno.ts
71
un_deno.ts
|
|
@ -39,6 +39,7 @@
|
|||
// Full-featured CLI matching un.c/un.py capabilities
|
||||
|
||||
const API_BASE = "https://api.unsandbox.com";
|
||||
const PORTAL_BASE = "https://unsandbox.com";
|
||||
const BLUE = "\x1b[34m";
|
||||
const RED = "\x1b[31m";
|
||||
const GREEN = "\x1b[32m";
|
||||
|
|
@ -92,8 +93,10 @@ async function apiRequest(
|
|||
method: string,
|
||||
data?: unknown,
|
||||
apiKey?: string,
|
||||
baseUrl?: string,
|
||||
): Promise<any> {
|
||||
const url = `${API_BASE}${endpoint}`;
|
||||
const base = baseUrl || API_BASE;
|
||||
const url = `${base}${endpoint}`;
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
|
|
@ -328,6 +331,69 @@ async function cmdSession(args: string[]) {
|
|||
);
|
||||
}
|
||||
|
||||
async function cmdKey(args: string[]) {
|
||||
const apiKey = getApiKey();
|
||||
let extend = false;
|
||||
|
||||
// Parse arguments
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--extend") {
|
||||
extend = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await apiRequest("/keys/validate", "POST", undefined, apiKey, PORTAL_BASE);
|
||||
|
||||
// Handle --extend flag
|
||||
if (extend) {
|
||||
const publicKey = result.public_key;
|
||||
if (publicKey) {
|
||||
const url = `${PORTAL_BASE}/keys/extend?pk=${publicKey}`;
|
||||
console.log(`${BLUE}Opening browser to extend key...${RESET}`);
|
||||
if (Deno.build.os === "darwin") {
|
||||
await new Deno.Command("open", { args: [url] }).output();
|
||||
} else if (Deno.build.os === "linux") {
|
||||
await new Deno.Command("xdg-open", { args: [url] }).output();
|
||||
} else if (Deno.build.os === "windows") {
|
||||
await new Deno.Command("cmd", { args: ["/c", "start", url] }).output();
|
||||
} else {
|
||||
console.log(`${YELLOW}Please open manually: ${url}${RESET}`);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
console.error(`${RED}Error: Could not retrieve public key${RESET}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if key is expired
|
||||
if (result.expired) {
|
||||
console.log(`${RED}Expired${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || "N/A"}`);
|
||||
console.log(`Tier: ${result.tier || "N/A"}`);
|
||||
console.log(`Expired: ${result.expires_at || "N/A"}`);
|
||||
console.log(`${YELLOW}To renew: Visit ${PORTAL_BASE}/keys/extend${RESET}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
// Valid key
|
||||
console.log(`${GREEN}Valid${RESET}`);
|
||||
console.log(`Public Key: ${result.public_key || "N/A"}`);
|
||||
console.log(`Tier: ${result.tier || "N/A"}`);
|
||||
console.log(`Status: ${result.status || "N/A"}`);
|
||||
console.log(`Expires: ${result.expires_at || "N/A"}`);
|
||||
console.log(`Time Remaining: ${result.time_remaining || "N/A"}`);
|
||||
console.log(`Rate Limit: ${result.rate_limit || "N/A"}`);
|
||||
console.log(`Burst: ${result.burst || "N/A"}`);
|
||||
console.log(`Concurrency: ${result.concurrency || "N/A"}`);
|
||||
} catch (e) {
|
||||
console.log(`${RED}Invalid${RESET}`);
|
||||
console.log(`Reason: ${e}`);
|
||||
Deno.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function cmdService(args: string[]) {
|
||||
const apiKey = getApiKey();
|
||||
let listMode = false;
|
||||
|
|
@ -509,6 +575,7 @@ async function main() {
|
|||
console.error("Usage: un_deno.ts [options] <source_file>");
|
||||
console.error(" un_deno.ts session [options]");
|
||||
console.error(" un_deno.ts service [options]");
|
||||
console.error(" un_deno.ts key [options]");
|
||||
Deno.exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -518,6 +585,8 @@ async function main() {
|
|||
await cmdSession(args.slice(1));
|
||||
} else if (firstArg === "service") {
|
||||
await cmdService(args.slice(1));
|
||||
} else if (firstArg === "key") {
|
||||
await cmdKey(args.slice(1));
|
||||
} else {
|
||||
await cmdExecute(args);
|
||||
}
|
||||
|
|
|
|||
148
un_inception.c
148
un_inception.c
|
|
@ -50,6 +50,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#define API_BASE "https://api.unsandbox.com"
|
||||
#define PORTAL_BASE "https://unsandbox.com"
|
||||
#define BLUE "\033[34m"
|
||||
#define RED "\033[31m"
|
||||
#define GREEN "\033[32m"
|
||||
|
|
@ -312,6 +313,142 @@ void cmd_session(int list, const char *kill, const char *shell, const char *netw
|
|||
printf("\n%sSession created%s\n", GREEN, RESET);
|
||||
}
|
||||
|
||||
void cmd_key(int extend, const char *api_key) {
|
||||
char cmd[4096];
|
||||
|
||||
snprintf(cmd, sizeof(cmd),
|
||||
"curl -s -X POST '%s/keys/validate' "
|
||||
"-H 'Content-Type: application/json' "
|
||||
"-H 'Authorization: Bearer %s'",
|
||||
PORTAL_BASE, api_key);
|
||||
|
||||
FILE *curl = popen(cmd, "r");
|
||||
if (!curl) {
|
||||
fprintf(stderr, "%sError running curl%s\n", RED, RESET);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char response[16384];
|
||||
size_t resp_len = fread(response, 1, sizeof(response) - 1, curl);
|
||||
response[resp_len] = 0;
|
||||
pclose(curl);
|
||||
|
||||
// Parse JSON response (simple string matching)
|
||||
char *public_key_start = strstr(response, "\"public_key\":\"");
|
||||
char public_key[256] = "";
|
||||
if (public_key_start) {
|
||||
public_key_start += 14;
|
||||
char *end = strchr(public_key_start, '"');
|
||||
if (end) {
|
||||
size_t len = end - public_key_start;
|
||||
if (len < sizeof(public_key)) {
|
||||
strncpy(public_key, public_key_start, len);
|
||||
public_key[len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle --extend flag
|
||||
if (extend) {
|
||||
if (public_key[0]) {
|
||||
printf("%sOpening browser to extend key...%s\n", BLUE, RESET);
|
||||
char open_cmd[512];
|
||||
#ifdef __APPLE__
|
||||
snprintf(open_cmd, sizeof(open_cmd), "open '%s/keys/extend?pk=%s'", PORTAL_BASE, public_key);
|
||||
#elif __linux__
|
||||
snprintf(open_cmd, sizeof(open_cmd), "xdg-open '%s/keys/extend?pk=%s'", PORTAL_BASE, public_key);
|
||||
#else
|
||||
snprintf(open_cmd, sizeof(open_cmd), "start '%s/keys/extend?pk=%s'", PORTAL_BASE, public_key);
|
||||
#endif
|
||||
system(open_cmd);
|
||||
return;
|
||||
} else {
|
||||
fprintf(stderr, "%sError: Could not retrieve public key%s\n", RED, RESET);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if expired
|
||||
if (strstr(response, "\"expired\":true")) {
|
||||
printf("%sExpired%s\n", RED, RESET);
|
||||
printf("Public Key: %s\n", public_key[0] ? public_key : "N/A");
|
||||
|
||||
char *tier_start = strstr(response, "\"tier\":\"");
|
||||
if (tier_start) {
|
||||
tier_start += 8;
|
||||
char *end = strchr(tier_start, '"');
|
||||
if (end) printf("Tier: %.*s\n", (int)(end - tier_start), tier_start);
|
||||
}
|
||||
|
||||
char *expires_start = strstr(response, "\"expires_at\":\"");
|
||||
if (expires_start) {
|
||||
expires_start += 14;
|
||||
char *end = strchr(expires_start, '"');
|
||||
if (end) printf("Expired: %.*s\n", (int)(end - expires_start), expires_start);
|
||||
}
|
||||
|
||||
printf("%sTo renew: Visit %s/keys/extend%s\n", YELLOW, PORTAL_BASE, RESET);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Valid key
|
||||
printf("%sValid%s\n", GREEN, RESET);
|
||||
printf("Public Key: %s\n", public_key[0] ? public_key : "N/A");
|
||||
|
||||
// Extract and print other fields
|
||||
char *tier_start = strstr(response, "\"tier\":\"");
|
||||
if (tier_start) {
|
||||
tier_start += 8;
|
||||
char *end = strchr(tier_start, '"');
|
||||
if (end) printf("Tier: %.*s\n", (int)(end - tier_start), tier_start);
|
||||
}
|
||||
|
||||
char *status_start = strstr(response, "\"status\":\"");
|
||||
if (status_start) {
|
||||
status_start += 10;
|
||||
char *end = strchr(status_start, '"');
|
||||
if (end) printf("Status: %.*s\n", (int)(end - status_start), status_start);
|
||||
}
|
||||
|
||||
char *expires_start = strstr(response, "\"expires_at\":\"");
|
||||
if (expires_start) {
|
||||
expires_start += 14;
|
||||
char *end = strchr(expires_start, '"');
|
||||
if (end) printf("Expires: %.*s\n", (int)(end - expires_start), expires_start);
|
||||
}
|
||||
|
||||
char *time_rem_start = strstr(response, "\"time_remaining\":\"");
|
||||
if (time_rem_start) {
|
||||
time_rem_start += 18;
|
||||
char *end = strchr(time_rem_start, '"');
|
||||
if (end) printf("Time Remaining: %.*s\n", (int)(end - time_rem_start), time_rem_start);
|
||||
}
|
||||
|
||||
char *rate_start = strstr(response, "\"rate_limit\":");
|
||||
if (rate_start) {
|
||||
rate_start += 13;
|
||||
char *end = strchr(rate_start, ',');
|
||||
if (!end) end = strchr(rate_start, '}');
|
||||
if (end) printf("Rate Limit: %.*s\n", (int)(end - rate_start), rate_start);
|
||||
}
|
||||
|
||||
char *burst_start = strstr(response, "\"burst\":");
|
||||
if (burst_start) {
|
||||
burst_start += 8;
|
||||
char *end = strchr(burst_start, ',');
|
||||
if (!end) end = strchr(burst_start, '}');
|
||||
if (end) printf("Burst: %.*s\n", (int)(end - burst_start), burst_start);
|
||||
}
|
||||
|
||||
char *conc_start = strstr(response, "\"concurrency\":");
|
||||
if (conc_start) {
|
||||
conc_start += 14;
|
||||
char *end = strchr(conc_start, ',');
|
||||
if (!end) end = strchr(conc_start, '}');
|
||||
if (end) printf("Concurrency: %.*s\n", (int)(end - conc_start), conc_start);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
|
|
@ -438,10 +575,21 @@ int main(int argc, char *argv[]) {
|
|||
fprintf(stderr, "Usage: %s [options] <source_file>\n", argv[0]);
|
||||
fprintf(stderr, " %s session [options]\n", argv[0]);
|
||||
fprintf(stderr, " %s service [options]\n", argv[0]);
|
||||
fprintf(stderr, " %s key [options]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Parse command
|
||||
if (strcmp(argv[1], "key") == 0) {
|
||||
int extend = 0;
|
||||
for (int i = 2; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--extend") == 0) extend = 1;
|
||||
else if (strcmp(argv[i], "-k") == 0 && i + 1 < argc) api_key = argv[++i];
|
||||
}
|
||||
cmd_key(extend, api_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strcmp(argv[1], "session") == 0) {
|
||||
int list = 0;
|
||||
const char *kill = NULL;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue