diff --git a/un.awk b/un.awk index 1c04a87..074ea67 100644 --- a/un.awk +++ b/un.awk @@ -169,6 +169,17 @@ function execute(filename , api_key) { # Clean up system("rm -f " tmp) + # Check for timestamp authentication errors + if (match(response, /timestamp/) && (match(response, /401/) || match(response, /expired/) || match(response, /invalid/))) { + print RED "Error: Request timestamp expired (must be within 5 minutes of server time)" RESET > "/dev/stderr" + print YELLOW "Your computer's clock may have drifted." RESET > "/dev/stderr" + print "Check your system time and sync with NTP if needed:" > "/dev/stderr" + print " Linux: sudo ntpdate -s time.nist.gov" > "/dev/stderr" + print " macOS: sudo sntp -sS time.apple.com" > "/dev/stderr" + print " Windows: w32tm /resync" > "/dev/stderr" + exit 1 + } + # Parse stdout from response (simple regex) if (match(response, /"stdout":"([^"]*)"/, arr)) { stdout = arr[1] diff --git a/un.cpp b/un.cpp index a514e51..3394ccc 100644 --- a/un.cpp +++ b/un.cpp @@ -109,6 +109,19 @@ string exec_curl(const string& cmd) { result += buffer; } pclose(pipe); + + // Check for timestamp authentication errors + if (result.find("timestamp") != string::npos && + (result.find("401") != string::npos || result.find("expired") != string::npos || result.find("invalid") != string::npos)) { + cerr << RED << "Error: Request timestamp expired (must be within 5 minutes of server time)" << RESET << endl; + cerr << YELLOW << "Your computer's clock may have drifted." << RESET << endl; + cerr << "Check your system time and sync with NTP if needed:" << endl; + cerr << " Linux: sudo ntpdate -s time.nist.gov" << endl; + cerr << " macOS: sudo sntp -sS time.apple.com" << endl; + cerr << " Windows: w32tm /resync" << endl; + exit(1); + } + return result; } diff --git a/un.cr b/un.cr index 7f3c3c4..6cb19b0 100644 --- a/un.cr +++ b/un.cr @@ -130,7 +130,17 @@ def api_request(endpoint : String, public_key : String, secret_key : String?, me JSON.parse(response.body) rescue ex - STDERR.puts "#{RED}Error: Request failed: #{ex.message}#{RESET}" + error_msg = ex.message || "" + if error_msg.downcase.includes?("timestamp") || (response && response.status_code == 401 && response.body.downcase.includes?("timestamp")) + STDERR.puts "#{RED}Error: Request timestamp expired (must be within 5 minutes of server time)#{RESET}" + STDERR.puts "#{YELLOW}Your computer's clock may have drifted.#{RESET}" + STDERR.puts "Check your system time and sync with NTP if needed:" + STDERR.puts " Linux: sudo ntpdate -s time.nist.gov" + STDERR.puts " macOS: sudo sntp -sS time.apple.com" + STDERR.puts " Windows: w32tm /resync" + else + STDERR.puts "#{RED}Error: Request failed: #{ex.message}#{RESET}" + end exit 1 end end diff --git a/un.d b/un.d index f918112..9b67077 100644 --- a/un.d +++ b/un.d @@ -121,7 +121,22 @@ string buildAuthHeaders(string method, string path, string body, string publicKe string execCurl(string cmd) { auto result = executeShell(cmd); - return result.output; + string output = result.output; + + // Check for timestamp authentication errors + import std.algorithm : canFind; + if (output.canFind("timestamp") && + (output.canFind("401") || output.canFind("expired") || output.canFind("invalid"))) { + stderr.writefln("%sError: Request timestamp expired (must be within 5 minutes of server time)%s", RED, RESET); + stderr.writefln("%sYour computer's clock may have drifted.%s", YELLOW, RESET); + stderr.writeln("Check your system time and sync with NTP if needed:"); + stderr.writeln(" Linux: sudo ntpdate -s time.nist.gov"); + stderr.writeln(" macOS: sudo sntp -sS time.apple.com"); + stderr.writeln(" Windows: w32tm /resync"); + exit(1); + } + + return output; } void cmdExecute(string sourceFile, string[] envs, bool artifacts, string network, int vcpu, string publicKey, string secretKey) { diff --git a/un.dart b/un.dart index 5f556d1..2b03262 100644 --- a/un.dart +++ b/un.dart @@ -172,6 +172,19 @@ Future> apiRequestCurl(String endpoint, String method, Stri } final response = result.stdout as String; + + // Check for timestamp authentication errors + if (response.toLowerCase().contains('timestamp') && + (response.contains('401') || response.toLowerCase().contains('expired') || response.toLowerCase().contains('invalid'))) { + stderr.writeln('${red}Error: Request timestamp expired (must be within 5 minutes of server time)$reset'); + stderr.writeln('${yellow}Your computer\'s clock may have drifted.$reset'); + stderr.writeln('Check your system time and sync with NTP if needed:'); + stderr.writeln(' Linux: sudo ntpdate -s time.nist.gov'); + stderr.writeln(' macOS: sudo sntp -sS time.apple.com'); + stderr.writeln(' Windows: w32tm /resync'); + exit(1); + } + return jsonDecode(response) as Map; } finally { await tempFile.delete(); diff --git a/un.groovy b/un.groovy index 3d1e889..f4dc222 100644 --- a/un.groovy +++ b/un.groovy @@ -168,6 +168,18 @@ def apiRequest(endpoint, method, data, publicKey, secretKey) { System.exit(1) } + // Check for timestamp authentication errors + if (output.toLowerCase().contains('timestamp') && + (output.contains('401') || output.toLowerCase().contains('expired') || output.toLowerCase().contains('invalid'))) { + System.err.println("${RED}Error: Request timestamp expired (must be within 5 minutes of server time)${RESET}") + System.err.println("${YELLOW}Your computer's clock may have drifted.${RESET}") + System.err.println("Check your system time and sync with NTP if needed:") + System.err.println(" Linux: sudo ntpdate -s time.nist.gov") + System.err.println(" macOS: sudo sntp -sS time.apple.com") + System.err.println(" Windows: w32tm /resync") + System.exit(1) + } + return output } finally { tempFile.delete() diff --git a/un.jl b/un.jl index 35bcbf9..fefb215 100755 --- a/un.jl +++ b/un.jl @@ -141,7 +141,17 @@ function api_request(endpoint::String, public_key::String, secret_key::String; m return JSON.parse(String(response.body)) catch e if isa(e, HTTP.ExceptionRequest.StatusError) - println(stderr, "$(RED)Error: HTTP $(e.status) - $(String(e.response.body))$(RESET)") + error_body = String(e.response.body) + if e.status == 401 && occursin("timestamp", lowercase(error_body)) + println(stderr, "$(RED)Error: Request timestamp expired (must be within 5 minutes of server time)$(RESET)") + println(stderr, "$(YELLOW)Your computer's clock may have drifted.$(RESET)") + println(stderr, "Check your system time and sync with NTP if needed:") + println(stderr, " Linux: sudo ntpdate -s time.nist.gov") + println(stderr, " macOS: sudo sntp -sS time.apple.com") + println(stderr, " Windows: w32tm /resync") + else + println(stderr, "$(RED)Error: HTTP $(e.status) - $(error_body)$(RESET)") + end else println(stderr, "$(RED)Error: Request failed: $e$(RESET)") end diff --git a/un.kt b/un.kt index 60ebd72..dd05a3d 100644 --- a/un.kt +++ b/un.kt @@ -530,6 +530,15 @@ fun apiRequest(endpoint: String, method: String, data: Map?, public if (connection.responseCode !in 200..299) { val error = connection.errorStream?.bufferedReader()?.readText() ?: "" + if (connection.responseCode == 401 && error.lowercase().contains("timestamp")) { + System.err.println("${RED}Error: Request timestamp expired (must be within 5 minutes of server time)${RESET}") + System.err.println("${YELLOW}Your computer's clock may have drifted.${RESET}") + System.err.println("Check your system time and sync with NTP if needed:") + System.err.println(" Linux: sudo ntpdate -s time.nist.gov") + System.err.println(" macOS: sudo sntp -sS time.apple.com") + System.err.println(" Windows: w32tm /resync") + exitProcess(1) + } throw RuntimeException("HTTP ${connection.responseCode} - $error") } diff --git a/un.nim b/un.nim index 97fe60b..b550fa2 100644 --- a/un.nim +++ b/un.nim @@ -98,6 +98,17 @@ proc buildAuthHeaders(meth: string, path: string, body: string, publicKey: strin proc execCurl(cmd: string): string = result = execProcess(cmd) + # Check for timestamp authentication errors + if result.contains("timestamp") and + (result.contains("401") or result.contains("expired") or result.contains("invalid")): + stderr.writeLine(RED & "Error: Request timestamp expired (must be within 5 minutes of server time)" & RESET) + stderr.writeLine(YELLOW & "Your computer's clock may have drifted." & RESET) + stderr.writeLine("Check your system time and sync with NTP if needed:") + stderr.writeLine(" Linux: sudo ntpdate -s time.nist.gov") + stderr.writeLine(" macOS: sudo sntp -sS time.apple.com") + stderr.writeLine(" Windows: w32tm /resync") + quit(1) + proc cmdExecute(sourceFile: string, envs: seq[string], artifacts: bool, network: string, vcpu: int, publicKey: string, secretKey: string) = let lang = detectLanguage(sourceFile) if lang == "": diff --git a/un.ps1 b/un.ps1 index b332709..5497e71 100644 --- a/un.ps1 +++ b/un.ps1 @@ -111,7 +111,17 @@ function Invoke-Api { } return $response } catch { - Write-Error "Error: $($_.Exception.Message)" + $errorMsg = $_.Exception.Message + if ($errorMsg -match "401" -and $errorMsg -match "timestamp") { + Write-Host "`e[31mError: Request timestamp expired (must be within 5 minutes of server time)`e[0m" -ForegroundColor Red + Write-Host "`e[33mYour computer's clock may have drifted.`e[0m" -ForegroundColor Yellow + Write-Host "Check your system time and sync with NTP if needed:" + Write-Host " Linux: sudo ntpdate -s time.nist.gov" + Write-Host " macOS: sudo sntp -sS time.apple.com" + Write-Host " Windows: w32tm /resync" + } else { + Write-Error "Error: $errorMsg" + } exit 1 } } diff --git a/un.rs b/un.rs index 69cdae3..22f6eb1 100644 --- a/un.rs +++ b/un.rs @@ -228,7 +228,20 @@ fn api_request(endpoint: &str, method: &str, body: Option<&str>, public_key: &st process::exit(1); } - String::from_utf8_lossy(&output.stdout).to_string() + let result = String::from_utf8_lossy(&output.stdout).to_string(); + + // Check for timestamp authentication errors + if result.contains("timestamp") && (result.contains("401") || result.contains("expired") || result.contains("invalid")) { + eprintln!("{}Error: Request timestamp expired (must be within 5 minutes of server time){}", RED, RESET); + eprintln!("{}Your computer's clock may have drifted.{}", YELLOW, RESET); + eprintln!("Check your system time and sync with NTP if needed:"); + eprintln!(" Linux: sudo ntpdate -s time.nist.gov"); + eprintln!(" macOS: sudo sntp -sS time.apple.com"); + eprintln!(" Windows: w32tm /resync"); + process::exit(1); + } + + result } fn cmd_execute( diff --git a/un.tcl b/un.tcl index df10833..3176936 100755 --- a/un.tcl +++ b/un.tcl @@ -151,8 +151,17 @@ proc api_request {endpoint method data public_key secret_key} { ::http::cleanup $token if {$status ne "ok" || ($ncode != 200 && $ncode != 201)} { - puts stderr "${::RED}Error: HTTP $ncode${::RESET}" - puts stderr $body + if {$ncode == 401 && [string match -nocase "*timestamp*" $body]} { + puts stderr "${::RED}Error: Request timestamp expired (must be within 5 minutes of server time)${::RESET}" + puts stderr "${::YELLOW}Your computer's clock may have drifted.${::RESET}" + puts stderr "Check your system time and sync with NTP if needed:" + puts stderr " Linux: sudo ntpdate -s time.nist.gov" + puts stderr " macOS: sudo sntp -sS time.apple.com" + puts stderr " Windows: w32tm /resync" + } else { + puts stderr "${::RED}Error: HTTP $ncode${::RESET}" + puts stderr $body + } exit 1 } diff --git a/un.ts b/un.ts index 7ec69da..e731e69 100644 --- a/un.ts +++ b/un.ts @@ -191,7 +191,16 @@ function apiRequest(endpoint: string, method: string = "GET", data: any = null, resolve(body); } } else { - console.error(`${RED}Error: HTTP ${res.statusCode} - ${body}${RESET}`); + if (res.statusCode === 401 && body.toLowerCase().includes('timestamp')) { + console.error(`${RED}Error: Request timestamp expired (must be within 5 minutes of server time)${RESET}`); + console.error(`${YELLOW}Your computer's clock may have drifted.${RESET}`); + console.error("Check your system time and sync with NTP if needed:"); + console.error(" Linux: sudo ntpdate -s time.nist.gov"); + console.error(" macOS: sudo sntp -sS time.apple.com"); + console.error(" Windows: w32tm /resync"); + } else { + console.error(`${RED}Error: HTTP ${res.statusCode} - ${body}${RESET}`); + } process.exit(1); } }); diff --git a/un.v b/un.v index 7185bb0..a07b765 100644 --- a/un.v +++ b/un.v @@ -97,7 +97,21 @@ fn escape_json(s string) string { fn exec_curl(cmd string) string { result := os.execute(cmd) - return result.output + output := result.output + + // Check for timestamp authentication errors + if output.contains('timestamp') && + (output.contains('401') || output.contains('expired') || output.contains('invalid')) { + eprintln('${red}Error: Request timestamp expired (must be within 5 minutes of server time)${reset}') + eprintln('${yellow}Your computer\'s clock may have drifted.${reset}') + eprintln('Check your system time and sync with NTP if needed:') + eprintln(' Linux: sudo ntpdate -s time.nist.gov') + eprintln(' macOS: sudo sntp -sS time.apple.com') + eprintln(' Windows: w32tm /resync') + exit(1) + } + + return output } fn extract_json_string(json string, key string) string {