text: import json
  import sys
  import re
  import time
  import traceback
  import requests
  from datetime import datetime
  
  from colorama import Fore
  
  
  def validate_dict_key(d, k):
      if d.get(k) is None:
          print(f'{Fore.RED}{k} is not set!{Fore.RESET}', file=sys.stderr, flush=True)
          return False
      return True
  
  class TsApi:
  
      def __init__(self, **kwargs):
          res = validate_dict_key(kwargs, 'org')
          res &= validate_dict_key(kwargs, 'api_url')
          res &= validate_dict_key(kwargs, 'auth_token')
          if not res:
              sys.exit(1)
          self.opts = kwargs
  
      @property
      def __api_url(self):
          return self.opts.get('api_url')
  
      @property
      def __request_defaults(self):
          return {
              'verify': self.opts.get('ignore_ssl') != True,
              'headers': self.__get_headers()
          }
  
      def __get_headers(self):
          headers = {'x-org-slug': self.opts.get('org')}
          ts_auth = self.opts.get('auth_token')
          if re.compile(r'^([a-z0-9]+-)+[a-z0-9]+$').match(ts_auth, re.IGNORECASE):
              headers['x-api-key'] = ts_auth
          else:
              headers['ts-auth-token'] = ts_auth
          return headers
  
      def upload_artifact(self, cfg, artifact_bytes):
          type_to_url = {
              'ids': 'ids',
              'master-script': 'master-scripts',
              'protocol': 'master-scripts',
              'task-script': 'task-scripts'
          }
          url = f'{self.__api_url}/artifact/{type_to_url[cfg.type]}/{cfg.namespace}/{cfg.slug}/{cfg.version}'
          r = requests.post(
              url,
              **self.__request_defaults,
              params={'force': 'true'} if cfg.force else {},
              data=artifact_bytes
          )
          if r.status_code < 400:
              return r.json()
          else:
              print(r.text, file=sys.stderr, flush=True)
              raise Exception(f'HTTP status: {r.status_code}, url: {r.url}')
  
      def get_task_script_build_info(self, id: str):
          url = f'{self.__api_url}/artifact/builds/{id}'
          r = requests.get(
              url,
              **self.__request_defaults
          )
          if r.status_code < 400:
              return r.json()
          else:
              print(r.text, file=sys.stderr, flush=True)
              raise Exception(f'HTTP status: {r.status_code}, url: {r.url}')
  
      def get_task_script_build_logs(self, id: str, params):
          url = f'{self.__api_url}/artifact/build-logs/{id}'
          r = requests.get(
              url,
              **self.__request_defaults,
              params={k:v for k,v in params.items() if v is not None}
          )
          if r.status_code < 400:
              return r.json()
          else:
              print(r.text, file=sys.stderr, flush=True)
              raise Exception(f'HTTP status: {r.status_code}, url: {r.url}')
  