dotfiles

My personal shell configs and stuff
git clone git://git.alex.balgavy.eu/dotfiles.git
Log | Files | Refs | Submodules | README | LICENSE

it2api (33202B)


      1 #!/usr/bin/env python3.7
      2 
      3 import argparse
      4 import asyncio
      5 import iterm2
      6 import logging
      7 import re
      8 import sys
      9 import traceback
     10 
     11 async def list_sessions(connection, args):
     12   a = await iterm2.async_get_app(connection)
     13   for w in a.terminal_windows:
     14     for t in w.tabs:
     15       sessions = t.sessions
     16       for s in sessions:
     17         print(s.pretty_str(), end='')
     18   print("")
     19   print("Buried sessions:")
     20   for s in a.buried_sessions:
     21     print(s.pretty_str(), end='')
     22 
     23 async def show_hierarchy(connection, args):
     24   a = await iterm2.async_get_app(connection)
     25   print(a.pretty_str())
     26 
     27 async def send_text(connection, args):
     28   a = await iterm2.async_get_app(connection)
     29   s = a.get_session_by_id(args.session)
     30   await s.async_send_text(args.text)
     31 
     32 async def create_tab(connection, args):
     33   a = await iterm2.async_get_app(connection)
     34   if args.window is not None:
     35     window_id = args.window
     36     try:
     37       window = next(window for window in a.terminal_windows if window.window_id == window_id)
     38       tab = await window.async_create_tab(profile=args.profile, command=args.command, index=args.index)
     39     except:
     40       print("bad window id {}".format(window_id))
     41       sys.exit(1)
     42   else:
     43     window = await iterm2.Window.async_create(connection, profile=args.profile, command=args.command)
     44     if not window:
     45         return
     46     tab = window.tabs[0]
     47   session = tab.sessions[0]
     48   print(session.pretty_str())
     49 
     50 async def split_pane(connection, args):
     51   a = await iterm2.async_get_app(connection)
     52   s = a.get_session_by_id(args.session)
     53   session = await s.async_split_pane(vertical=args.vertical, before=args.before, profile=args.profile)
     54   print(session.pretty_str())
     55 
     56 async def get_buffer(connection, args):
     57   a = await iterm2.async_get_app(connection)
     58   s = a.get_session_by_id(args.session)
     59   contents = await s.async_get_screen_contents()
     60   for i in range(contents.number_of_lines):
     61     line = contents.line(i)
     62     print(line.string)
     63 
     64 async def get_prompt(connection, args):
     65   a = await iterm2.async_get_app(connection)
     66   s = a.get_session_by_id(args.session)
     67   result = await iterm2.async_get_last_prompt(connection, s.session_id)
     68   print("working_directory: \"{}\"".format(result.working_directory))
     69   print("command: \"{}\"".format(result.command))
     70 
     71 def profile_property_type_map():
     72   map = {
     73       "allow_title_reporting":                       "bool",
     74       "allow_title_setting":                         "bool",
     75       "ambiguous_double_width":                      "bool",
     76       "ansi_0_color":                                "color",
     77       "ansi_10_color":                               "color",
     78       "ansi_11_color":                               "color",
     79       "ansi_12_color":                               "color",
     80       "ansi_13_color":                               "color",
     81       "ansi_14_color":                               "color",
     82       "ansi_15_color":                               "color",
     83       "ansi_1_color":                                "color",
     84       "ansi_2_color":                                "color",
     85       "ansi_3_color":                                "color",
     86       "ansi_4_color":                                "color",
     87       "ansi_5_color":                                "color",
     88       "ansi_6_color":                                "color",
     89       "ansi_7_color":                                "color",
     90       "ansi_8_color":                                "color",
     91       "ansi_9_color":                                "color",
     92       "answerback_string":                           "str",
     93       "application_keypad_allowed":                  "bool",
     94       "ascii_anti_aliased":                          "bool",
     95       "ascii_ligatures":                             "bool",
     96       "background_color":                            "color",
     97       "background_image_is_tiled":                   "bool",
     98       "badge_color":                                 "color",
     99       "badge_text":                                  "str",
    100       "blend":                                       "float",
    101       "blink_allowed":                               "bool",
    102       "blinking_cursor":                             "bool",
    103       "blur":                                        "float",
    104       "blur_radius":                                 "float",
    105       "bm_growl":                                    "bool",
    106       "bold_color":                                  "color",
    107       "character_encoding":                          "int",
    108       "close_sessions_on_end":                       "bool",
    109       "cursor_boost":                                "float",
    110       "cursor_color":                                "color",
    111       "cursor_guide_color":                          "color",
    112       "cursor_text_color":                           "color",
    113       "cursor_type":                                 "int",
    114       "disable_printing":                            "bool",
    115       "disable_smcup_rmcup":                         "bool",
    116       "disable_window_resizing":                     "bool",
    117       "flashing_bell":                               "bool",
    118       "foreground_color":                            "color",
    119       "horizontal_spacing":                          "float",
    120       "idle_code":                                   "int",
    121       "idle_period":                                 "float",
    122       "link_color":                                  "color",
    123       "minimum_contrast":                            "float",
    124       "mouse_reporting":                             "bool",
    125       "mouse_reporting_allow_mouse_wheel":           "bool",
    126       "name":                                        "str",
    127       "non_ascii_anti_aliased":                      "bool",
    128       "non_ascii_ligatures":                         "bool",
    129       "only_the_default_bg_color_uses_transparency": "bool",
    130       "left_option_key_sends":                       "int",
    131       "place_prompt_at_first_column":                "bool",
    132       "prompt_before_closing":                       "bool",
    133       "reduce_flicker":                              "bool",
    134       "right_option_key_sends":                      "int",
    135       "scrollback_in_alternate_screen":              "bool",
    136       "scrollback_lines":                            "int",
    137       "scrollback_with_status_bar":                  "bool",
    138       "selected_text_color":                         "color",
    139       "selection_color":                             "color",
    140       "send_bell_alert":                             "bool",
    141       "send_code_when_idle":                         "bool",
    142       "send_idle_alert":                             "bool",
    143       "send_new_output_alert":                       "bool",
    144       "send_session_ended_alert":                    "bool",
    145       "send_terminal_generated_alerts":              "bool",
    146       "session_close_undo_timeout":                  "float",
    147       "show_mark_indicators":                        "bool",
    148       "silence_bell":                                "bool",
    149       "smart_cursor_color":                          "color",
    150       "smart_cursor_color":                          "color",
    151       "sync_title":                                  "str",
    152       "tab_color":                                   "color",
    153       "thin_strokes":                                "int",
    154       "transparency":                                "float",
    155       "underline_color":                             "color",
    156       "unicode_normalization":                       "int",
    157       "unicode_version":                             "int",
    158       "unlimited_scrollback":                        "bool",
    159       "use_bold_font":                               "bool",
    160       "use_bright_bold":                             "bool",
    161       "use_cursor_guide":                            "bool",
    162       "use_italic_font":                             "bool",
    163       "use_non_ascii_font":                          "bool",
    164       "use_tab_color":                               "bool",
    165       "use_underline_color":                         "bool",
    166       "vertical_spacing":                            "float",
    167       "visual_bell":                                 "bool",
    168       "triggers":                                    "dict",
    169       "smart_selection_rules":                       "list",
    170       "semantic_history":                            "dict",
    171       "automatic_profile_switching_rules":           "list",
    172       "advanced_working_directory_window_setting":   "string",
    173       "advanced_working_directory_window_directory": "string",
    174       "advanced_working_directory_tab_setting":      "string",
    175       "advanced_working_directory_tab_directory":    "string",
    176       "advanced_working_directory_pane_setting":     "string",
    177       "advanced_working_directory_pane_directory":   "string",
    178       "normal_font":                                 "string",
    179       "non_ascii_font":                              "string",
    180       "background_image_location":                   "string",
    181       "key_mappings":                                "dict",
    182       "touchbar_mappings":                           "dict" }
    183   return map
    184 
    185 def profile_properties():
    186   return list(profile_property_type_map().keys())
    187 
    188 def profile_property_type(key):
    189   return profile_property_type_map()[key]
    190 
    191 async def get_profile_property(connection, args):
    192   a = await iterm2.async_get_app(connection)
    193   s = a.get_session_by_id(args.session)
    194   profile = await s.async_get_profile()
    195   if args.keys is not None:
    196     keys = args.keys.split(",")
    197   else:
    198     keys = profile_properties()
    199   for prop in keys:
    200     fname = prop
    201     value = getattr(profile, fname)
    202     print("{}: {}".format(prop, value))
    203 
    204 def encode_property_value(key, value):
    205   type = profile_property_type(key)
    206   if type == "bool":
    207     assert value == "true" or value == "false"
    208     return value == "true"
    209   elif type == "str":
    210     return value
    211   elif type == "float":
    212     return float(value)
    213   elif type == "int":
    214     return int(value)
    215   elif type == "dict" or type == "list":
    216     class TypeNotSupportedException(Exception): Pass
    217     raise TypeNotSupportedException("this property's type is not supported")
    218   elif type == "color":
    219     # Accepted values look like: "(0,0,0,255 sRGB)"
    220     regex = r"\(([0-9]+), *([0-9]+), *([0-9]+), *([0-9]+)  *([A-Za-z]+)\)"
    221     match = re.search(regex, value)
    222     assert match is not None
    223     return iterm2.Color(
    224         float(match.group(1)),
    225         float(match.group(2)),
    226         float(match.group(3)),
    227         float(match.group(4)),
    228         iterm2.ColorSpace(match.group(5)))
    229 
    230 async def set_profile_property(connection, args):
    231   a = await iterm2.async_get_app(connection)
    232   s = a.get_session_by_id(args.session)
    233 
    234   encoded_value = encode_property_value(args.key, args.value)
    235   profile = await s.async_get_profile()
    236   fname = "async_set_" + args.key
    237   f = getattr(profile, fname)
    238   await f(encoded_value)
    239 
    240 async def read(connection, args):
    241   a = await iterm2.async_get_app(connection)
    242   s = a.get_session_by_id(args.session)
    243   if args.mode == "char":
    244     async with iterm2.KeystrokeMonitor(connection) as mon:
    245       keystroke = await mon.async_get()
    246       print(keystroke)
    247   elif args.mode == "line":
    248     async with s.get_keystroke_reader() as reader:
    249       eol = False
    250       line = ""
    251       while not eol:
    252         k = await reader.get()
    253         for e in k:
    254           c = e.characters
    255           if c == "\r" or c == "\n":
    256             eol = True
    257             break
    258           line += c
    259 
    260       print(line)
    261 
    262 async def get_window_property(connection, args):
    263   a = await iterm2.async_get_app(connection)
    264   w = a.get_window_by_id(args.id)
    265   if w is None:
    266     print("bad window ID")
    267   else:
    268     if args.name == "frame":
    269       frame = await w.async_get_frame()
    270       print("{},{},{},{}".format(frame.origin.x,frame.origin.y,frame.size.width,frame.size.height))
    271     elif args.name == "fullscreen":
    272       print(await w.async_get_fullscreen(connection))
    273 
    274 async def set_window_property(connection, args):
    275   a = await iterm2.async_get_app(connection)
    276   w = a.get_window_by_id(args.id)
    277   if w is None:
    278     print("bad window ID")
    279   else:
    280     if args.name == "frame":
    281       parts = args.value.split(",")
    282       frame = iterm2.Frame(iterm2.Point(int(parts[0]), int(parts[1])), iterm2.Size(int(parts[2]), int(parts[3])))
    283       await w.async_set_frame(frame)
    284     elif args.name == "fullscreen":
    285       await w.async_set_fullscreen(args.value == "true")
    286 
    287 async def inject(connection, args):
    288   a = await iterm2.async_get_app(connection)
    289   s = a.get_session_by_id(args.session)
    290   if s is None:
    291     print("bad session ID")
    292   else:
    293     await s.async_inject(args.data.encode())
    294 
    295 async def activate(connection, args):
    296   a = await iterm2.async_get_app(connection)
    297   if args.mode == "session":
    298     s = a.get_session_by_id(args.id)
    299     if s is None:
    300       print("bad session ID")
    301     else:
    302       await s.async_activate()
    303   elif args.mode == "tab":
    304     t = a.get_tab_by_id(args.id)
    305     if t is None:
    306       print("bad tab ID")
    307     else:
    308       await t.async_select()
    309   elif args.mode == "window":
    310     w = a.get_window_by_id(args.id)
    311     if w is None:
    312       print("bad window ID")
    313     else:
    314       await w.async_activate()
    315 
    316 async def activate_app(connection, args):
    317   a = await iterm2.async_get_app(connection)
    318   await a.async_activate(raise_all_windows=args.raise_all_windows, ignoring_other_apps=args.ignoring_other_apps)
    319 
    320 async def set_variable(connection, args):
    321   a = await iterm2.async_get_app(connection)
    322   if args.session:
    323     s = a.get_session_by_id(args.session)
    324     if s is None:
    325       print("bad session ID")
    326       return
    327     await s.async_set_variable(args.name, args.value)
    328   elif args.tab:
    329     t = a.get_tab_by_id(args.tab)
    330     if t is None:
    331       print("bad tab ID")
    332       return
    333     await t.async_set_variable(args.name, args.value)
    334   else:
    335     await a.async_set_variable(args.name, args.value)
    336 
    337 async def get_variable(connection, args):
    338   a = await iterm2.async_get_app(connection)
    339   if args.session:
    340     s = a.get_session_by_id(args.session)
    341     if s is None:
    342       print("bad session ID")
    343       return
    344     value = await s.async_get_variable(args.name)
    345     print(value)
    346   elif args.tab:
    347     t = a.get_tab_by_id(args.tab)
    348     if t is None:
    349       print("bad tab ID")
    350       return
    351     value = await t.async_get_variable(args.name)
    352     print(value)
    353   else:
    354     value = await a.async_get_variable(args.name)
    355     print(value)
    356 
    357 async def list_variables(connection, args):
    358   a = await iterm2.async_get_app(connection)
    359   if args.session:
    360     s = a.get_session_by_id(args.session)
    361     if s is None:
    362       print("bad session ID")
    363       return
    364     value = await s.async_get_variable("*")
    365     for name in value:
    366       print(name)
    367   elif args.tab:
    368     t = a.get_tab_by_id(args.tab)
    369     if t is None:
    370       print("bad tab ID")
    371       return
    372     value = await t.async_get_variable("*")
    373     for name in value:
    374       print(name)
    375   else:
    376     value = await a.async_get_variable("*")
    377     for name in value:
    378       print(name)
    379 
    380 async def saved_arrangement(connection, args):
    381   if args.window is not None:
    382     a = await iterm2.async_get_app(connection)
    383     w = a.get_window_by_id(args.window)
    384     if w is None:
    385       print("bad window ID")
    386       return
    387     if args.action == "save":
    388       await w.async_save_window_as_arrangement(args.name)
    389     elif args.action == "restore":
    390       await w.async_restore_window_arrangement(args.name)
    391   else:
    392     a = await iterm2.async_get_app(connection)
    393     if args.action == "save":
    394       await a.async_save_window_arrangement(args.name)
    395     elif args.action == "restore":
    396       await a.async_restore_window_arrangement(args.name)
    397 
    398 async def show_focus(connection, args):
    399   a = await iterm2.async_get_app(connection)
    400   if a.app_active:
    401     print("App is active")
    402   w = a.current_terminal_window
    403   print("Key window: {}".format(w.window_id))
    404   print("")
    405   for w in a.terminal_windows:
    406     t = a.get_tab_by_id(w.selected_tab_id)
    407     print("Selected tab in {}: {}".format(w.window_id, t.tab_id))
    408     s = a.get_session_by_id(t.active_session_id)
    409     print("  Active session is: {}".format(s.pretty_str()))
    410 
    411 async def list_profiles(connection, args):
    412   guids = args.guids.split(",") if args.guids is not None else None
    413   properties = args.properties.split(",") if args.properties is not None else None
    414   profiles = await iterm2.PartialProfile.async_query(connection, guids=guids, properties=properties)
    415   for profile in profiles:
    416     keys = list(profile.all_properties.keys())
    417     keys.sort()
    418     for k in keys:
    419       v = profile.all_properties[k]
    420       print("{}: {}".format(k, v))
    421     print("")
    422 
    423 async def set_grid_size(connection, args):
    424   a = await iterm2.async_get_app(connection)
    425   s = a.get_session_by_id(args.session)
    426   await s.async_set_grid_size(iterm2.Size(args.width, args.height))
    427 
    428 async def list_tmux_connections(connection, args):
    429   connections = await iterm2.async_get_tmux_connections(connection)
    430   for connection in connections:
    431     print("Connection ID: {}\nOwning session: {}".format(connection.connection_id, connection.owning_session))
    432 
    433 async def send_tmux_command(connection, args):
    434   connections = await iterm2.async_get_tmux_connections(connection)
    435   ids = []
    436   for connection in connections:
    437     if connection.connection_id == args.connection_id:
    438       print(await connection.async_send_command(args.command))
    439       return;
    440     ids.append(connection.connection_id)
    441   print("No connection with id {} found. Have: {}".format(args.connection_id, ", ".join(ids)))
    442 
    443 async def set_tmux_window_visible(connection, args):
    444   connections = await iterm2.async_get_tmux_connections(connection)
    445   ids = []
    446   for connection in connections:
    447     if connection.connection_id == args.connection_id:
    448       await connection.async_set_tmux_window_visible(args.window_id, args.visible)
    449       return;
    450     ids.append(connection.connection_id)
    451   print("No connection with id {} found. Have: {}".format(args.connection_id, ", ".join(ids)))
    452 
    453 async def sort_tabs(connection, args):
    454   app = await iterm2.async_get_app(connection)
    455   for w in app.terminal_windows:
    456     tabs = w.tabs
    457     for t in tabs:
    458       t.tab_name = await t.async_get_variable("currentSession.session.name")
    459     def tab_name(t):
    460       return t.tab_name
    461     sorted_tabs = sorted(tabs, key=tab_name)
    462     await w.async_set_tabs(sorted_tabs)
    463 
    464 async def list_color_presets(connection, args):
    465   presets = await iterm2.ColorPreset.async_get_list(connection)
    466   for preset in presets:
    467     print(preset)
    468 
    469 async def set_color_preset(connection, args):
    470   preset = await iterm2.ColorPreset.async_get(connection, args.preset)
    471   profiles = await iterm2.PartialProfile.async_query(connection, properties=['Guid', 'Name'])
    472   for partial in profiles:
    473     if partial.name == args.profile:
    474       profile = await partial.async_get_full_profile()
    475       await profile.async_set_color_preset(preset)
    476 
    477 async def monitor_variable(connection, args):
    478   if args.session:
    479     scope = iterm2.VariableScopes.SESSION
    480     identifier = args.session
    481   elif args.tab:
    482     scope = iterm2.VariableScopes.TAB
    483     identifier = args.tab
    484   elif args.window:
    485     scope = iterm2.VariableScopes.WINDOW
    486     identifier = args.window
    487   elif args.app:
    488     scope = iterm2.VariableScopes.APP
    489     identifier = ''
    490   else:
    491     assert False
    492   async with iterm2.VariableMonitor(connection, scope, args.name, identifier) as monitor:
    493     value = await monitor.async_get()
    494     print(f"New value: {value}")
    495 
    496 async def monitor_focus(connection, args):
    497     async with iterm2.FocusMonitor(connection) as monitor:
    498         update = await monitor.async_get_next_update()
    499         print("Update: {}".format(update))
    500 
    501 async def set_cursor_color(connection, args):
    502     a = await iterm2.async_get_app(connection)
    503     s = a.get_session_by_id(args.session)
    504     partial = iterm2.LocalWriteOnlyProfile()
    505     r, g, b = list(map(int, args.color.split(",")))
    506     c = iterm2.Color(r, g, b)
    507     partial.set_cursor_color(c)
    508     await s.async_set_profile_properties(partial)
    509 
    510 async def monitor_screen(connection, args):
    511     a = await iterm2.async_get_app(connection)
    512     s = a.get_session_by_id(args.session)
    513     async with s.get_screen_streamer() as streamer:
    514         done = False
    515         while not done:
    516             contents = await streamer.async_get()
    517             for i in range(contents.number_of_lines):
    518                 line = contents.line(i)
    519                 if args.query in line.string:
    520                     return
    521     
    522 async def show_selection(connection, args):
    523     a = await iterm2.async_get_app(connection)
    524     s = a.get_session_by_id(args.session)
    525     selection = await s.async_get_selection()
    526     for sub in selection.subSelections:
    527         print("Sub selection: {}".format(await sub.async_get_string(connection, s.session_id)))
    528     print("Text: {}".format(await selection.async_get_string(connection, s.session_id, s.grid_size.width)))
    529 
    530 def make_parser():
    531   parser = argparse.ArgumentParser(description='iTerm2 CLI')
    532   subparsers = parser.add_subparsers(help='Commands')
    533 
    534   list_sessions_parser = subparsers.add_parser("list-sessions", help="List sessions")
    535   list_sessions_parser.set_defaults(func=list_sessions)
    536 
    537   show_hierarchy_parser = subparsers.add_parser("show-hierarchy", help="Show all windows, tabs, and sessions")
    538   show_hierarchy_parser.set_defaults(func=show_hierarchy)
    539 
    540   send_text_parser = subparsers.add_parser("send-text", help="Send text as though the user had typed it")
    541   send_text_parser.add_argument('session', type=str, help='Session ID')
    542   send_text_parser.add_argument("text", type=str, help='Text to send')
    543   send_text_parser.set_defaults(func=send_text)
    544 
    545   create_tab_parser = subparsers.add_parser("create-tab", help="Create a new tab or window")
    546   create_tab_parser.add_argument('--profile', type=str, nargs='?', help='Profile name')
    547   create_tab_parser.add_argument('--window', type=str, nargs='?', help='Window ID')
    548   create_tab_parser.add_argument('--index', type=int, nargs='?', help='Desired tab index')
    549   create_tab_parser.add_argument('--command', type=str, nargs='?', help='Command')
    550   create_tab_parser.set_defaults(func=create_tab)
    551 
    552   split_pane_parser = subparsers.add_parser("split-pane", help="Split a pane into two")
    553   split_pane_parser.add_argument('session', type=str, help='Session ID')
    554   split_pane_parser.add_argument('--vertical', action='store_true', help='Split vertically?', default=False)
    555   split_pane_parser.add_argument('--before', action='store_true', help='Spilt left or above target', default=False)
    556   split_pane_parser.add_argument('--profile', type=str, nargs='?', help='Profile name')
    557   split_pane_parser.set_defaults(func=split_pane)
    558 
    559   get_buffer_parser = subparsers.add_parser("get-buffer", help="Get screen contents")
    560   get_buffer_parser.add_argument("session", type=str, help="Session ID")
    561   get_buffer_parser.set_defaults(func=get_buffer)
    562 
    563   get_prompt_parser = subparsers.add_parser("get-prompt", help="Get info about prompt, if available. Gives either the current prompt or the last prompt if a command is being run. Requires shell integration for prompt detection.")
    564   get_prompt_parser.add_argument("session", type=str, help="Session ID")
    565   get_prompt_parser.set_defaults(func=get_prompt)
    566 
    567   get_profile_property_parser = subparsers.add_parser("get-profile-property", help="Get a session's profile settings")
    568   get_profile_property_parser.add_argument("session", type=str, help="Session ID")
    569   get_profile_property_parser.add_argument("keys", type=str, nargs='?', help="Comma separated keys. Omit to get all. Valid keys are: " + ", ".join(profile_properties()))
    570   get_profile_property_parser.set_defaults(func=get_profile_property)
    571 
    572   set_profile_parser = subparsers.add_parser("set-profile-property", help="Set a session's profile setting")
    573   set_profile_parser.add_argument("session", type=str, help="Session ID")
    574   set_profile_parser.add_argument("key", type=str, help="Key to set. Valid keys are: " + ", ".join(profile_properties()))
    575   set_profile_parser.add_argument("value", type=str, help="New value.")
    576   set_profile_parser.set_defaults(func=set_profile_property)
    577 
    578   read_parser = subparsers.add_parser("read", help="Wait for a input.")
    579   read_parser.add_argument("session", type=str, help="Session ID")
    580   read_parser.add_argument("mode", type=str, help="What to read", choices=[ "char", "line" ])
    581   read_parser.set_defaults(func=read)
    582 
    583   get_window_property_parser = subparsers.add_parser("get-window-property", help="Get a property of a window")
    584   get_window_property_parser.add_argument("id", type=str, help="Window ID")
    585   get_window_property_parser.add_argument("name", type=str, help="Property name", choices=["frame", "fullscreen"])
    586   get_window_property_parser.set_defaults(func=get_window_property)
    587 
    588   set_window_property_parser = subparsers.add_parser("set-window-property", help="Set a property of a window")
    589   set_window_property_parser.add_argument("id", type=str, help="Window ID")
    590   set_window_property_parser.add_argument("name", type=str, help="Property name", choices=["frame", "fullscreen"])
    591   set_window_property_parser.add_argument("value", type=str, help="New value. For frame: x,y,width,height; for fullscreen: true or false")
    592   set_window_property_parser.set_defaults(func=set_window_property)
    593 
    594   inject_parser = subparsers.add_parser("inject", help="Inject a string as though it were program output")
    595   inject_parser.add_argument("session", type=str, help="Session ID")
    596   inject_parser.add_argument("data", type=str, help="Data to inject")
    597   inject_parser.set_defaults(func=inject)
    598 
    599   activate_parser = subparsers.add_parser("activate", help="Activate a session, tab, or window.")
    600   activate_parser.add_argument("mode", type=str, help="What kind of object to activate", choices=["session", "tab", "window"])
    601   activate_parser.add_argument("id", type=str, help="ID of object to activate")
    602   activate_parser.set_defaults(func=activate)
    603 
    604   activate_app_parser = subparsers.add_parser("activate-app", help="Activate the app")
    605   activate_app_parser.add_argument('--raise_all_windows', action='store_true', help='Raise all windows?', default=False)
    606   activate_app_parser.add_argument('--ignoring_other_apps', action='store_true', help='Activate ignoring other apps (may steal focus)', default=False)
    607   activate_app_parser.set_defaults(func=activate_app)
    608 
    609   set_variable_parser = subparsers.add_parser("set-variable", help="Set a user-defined variable in a session. See Badges documentation for details.")
    610   set_variable_parser.add_argument("--session", type=str, nargs='?', help="Session ID")
    611   set_variable_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID")
    612   set_variable_parser.add_argument("name", type=str, help="Variable name. Starts with \"user.\"")
    613   set_variable_parser.add_argument("value", type=str, help="New value")
    614   set_variable_parser.set_defaults(func=set_variable)
    615 
    616   get_variable_parser = subparsers.add_parser("get-variable", help="Get a variable in a session. See Badges documentation for details.")
    617   get_variable_parser.add_argument("--session", type=str, nargs='?', help="Session ID")
    618   get_variable_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID")
    619   get_variable_parser.add_argument("name", type=str, help="Variable name. Starts with \"user.\"")
    620   get_variable_parser.set_defaults(func=get_variable)
    621 
    622   list_variables_parser = subparsers.add_parser("list-variables", help="Lists variable names available in a session.")
    623   list_variables_parser.add_argument("--session", type=str, nargs='?', help="Session ID")
    624   list_variables_parser.add_argument("--tab", type=str, nargs='?', help="Tab ID")
    625   list_variables_parser.set_defaults(func=list_variables)
    626 
    627   saved_arrangement_parser = subparsers.add_parser("saved-arrangement", help="Saves and restores window arrangements")
    628   saved_arrangement_parser.add_argument("action", type=str, help="Action to perform", choices=["save", "restore"])
    629   saved_arrangement_parser.add_argument("name", type=str, help="Arrangement name")
    630   saved_arrangement_parser.add_argument('--window', type=str, nargs='?', help='Window ID to save/restore to')
    631   saved_arrangement_parser.set_defaults(func=saved_arrangement)
    632 
    633   show_focus_parser = subparsers.add_parser("show-focus", help="Show active windows, tabs, and panes")
    634   show_focus_parser.set_defaults(func=show_focus)
    635 
    636   list_profiles_parser = subparsers.add_parser("list-profiles", help="List profiles")
    637   list_profiles_parser.add_argument("--guids", type=str, nargs='?', help="Comma-delimited list of profiles to list. Omit to get all of them.")
    638   list_profiles_parser.add_argument("--properties", type=str, nargs='?', help="Comma-delimited list of properties to request. Omit to get all of them.")
    639   list_profiles_parser.set_defaults(func=list_profiles)
    640 
    641   set_grid_size_parser = subparsers.add_parser("set-grid-size", help="Set size of session")
    642   set_grid_size_parser.add_argument("session", type=str, help="Session ID")
    643   set_grid_size_parser.add_argument("width", type=int, help="Width in columns")
    644   set_grid_size_parser.add_argument("height", type=int, help="Height in rows")
    645   set_grid_size_parser.set_defaults(func=set_grid_size)
    646 
    647   list_tmux_connections_parser = subparsers.add_parser("list-tmux-connections", help="List tmux integration connections")
    648   list_tmux_connections_parser.set_defaults(func=list_tmux_connections)
    649 
    650   send_tmux_command_parser = subparsers.add_parser("send-tmux-command", help="Send a tmux command to a tmux integration connection")
    651   send_tmux_command_parser.add_argument("connection_id", type=str, help="tmux connection ID")
    652   send_tmux_command_parser.add_argument("command", type=str, help="Command to send")
    653   send_tmux_command_parser.set_defaults(func=send_tmux_command)
    654 
    655   set_tmux_window_visible_parser = subparsers.add_parser("set-tmux-window-visible", help="Show or hide a tmux integration window (represented as a tab in iTerm2)")
    656   set_tmux_window_visible_parser.add_argument("connection_id", type=str, help="tmux connection ID")
    657   set_tmux_window_visible_parser.add_argument("window_id", type=str, help="tmux window ID (number)")
    658   set_tmux_window_visible_parser.add_argument('--visible', dest='visible', action='store_true')
    659   set_tmux_window_visible_parser.add_argument('--no-visible', dest='visible', action='store_false')
    660   set_tmux_window_visible_parser.set_defaults(visible=True)
    661   set_tmux_window_visible_parser.set_defaults(func=set_tmux_window_visible)
    662 
    663   sort_tabs_parser = subparsers.add_parser("sort-tabs", help="Sort tabs alphabetically by name")
    664   sort_tabs_parser.set_defaults(func=sort_tabs)
    665 
    666   list_color_presets_parser = subparsers.add_parser("list-color-presets", help="Lists names of color presets")
    667   list_color_presets_parser.set_defaults(func=list_color_presets)
    668 
    669   set_color_preset_parser = subparsers.add_parser("set-color-preset", help="Lists names of color presets")
    670   set_color_preset_parser.add_argument("profile", type=str, help="Profile name")
    671   set_color_preset_parser.add_argument("preset", type=str, help="Color preset name")
    672   set_color_preset_parser.set_defaults(func=set_color_preset)
    673 
    674   monitor_variable_parser = subparsers.add_parser("monitor-variable", help="Monitor changes to a variable")
    675   monitor_variable_parser.add_argument("name", type=str, help="variable name")
    676   monitor_variable_parser.add_argument('--session', type=str, nargs='?', help='Session ID for the variable scope')
    677   monitor_variable_parser.add_argument('--tab', type=str, nargs='?', help='Tab ID for the variable scope')
    678   monitor_variable_parser.add_argument('--window', type=str, nargs='?', help='Window ID for the variable scope')
    679   monitor_variable_parser.add_argument('--app', action='store_true', help='App scope', default=False)
    680   monitor_variable_parser.set_defaults(func=monitor_variable)
    681 
    682   monitor_focus_parser = subparsers.add_parser("monitor-focus", help="Monitor changes to focus")
    683   monitor_focus_parser.set_defaults(func=monitor_focus)
    684 
    685   set_cursor_color_parser = subparsers.add_parser("set-cursor-color", help="Set cursor color")
    686   set_cursor_color_parser.add_argument("session", type=str, help="Session ID")
    687   set_cursor_color_parser.add_argument("color", type=str, help="Color as red,green,blue where each value is in 0-255")
    688   set_cursor_color_parser.set_defaults(func=set_cursor_color)
    689 
    690   monitor_screen_parser = subparsers.add_parser("monitor-screen", help="Monitor screen contents")
    691   monitor_screen_parser.add_argument("session", type=str, help="Session ID")
    692   monitor_screen_parser.add_argument("query", type=str, help="Stop when this text is seen")
    693   monitor_screen_parser.set_defaults(func=monitor_screen)
    694 
    695   show_selection_parser = subparsers.add_parser("show-selection", help="Shows the selected text in a session")
    696   show_selection_parser.add_argument("session", type=str, help="Session ID")
    697   show_selection_parser.set_defaults(func=show_selection)
    698 
    699   return parser
    700 
    701 def main(argv):
    702   logging.basicConfig()
    703 
    704   parser = make_parser()
    705   args = parser.parse_args(argv[1:])
    706   if "func" not in args:
    707     print(parser.format_help())
    708     raise argparse.ArgumentTypeError('Missing command')
    709 
    710   async def wrapper(connection):
    711     try:
    712       await args.func(connection, args)
    713     except Exception as e:
    714       print(traceback.format_exc())
    715 
    716   iterm2.run_until_complete(wrapper)
    717 
    718 if __name__ == "__main__":
    719   try:
    720     main(sys.argv)
    721   except Exception as e:
    722     print(traceback.format_exc())
    723     sys.exit(1)