diff --git a/src/main/resources/assets/openpython/opos/.prop b/src/main/resources/assets/openpython/opos/.prop
new file mode 100644
index 00000000..9ba21168
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/.prop
@@ -0,0 +1 @@
+{label="OpenPython OS", reboot=true, setlabel=true, setboot=true}
\ No newline at end of file
diff --git a/src/main/resources/assets/openpython/opos/boot/01_basic.py b/src/main/resources/assets/openpython/opos/boot/01_basic.py
index d0f3ad5c..bb431423 100644
--- a/src/main/resources/assets/openpython/opos/boot/01_basic.py
+++ b/src/main/resources/assets/openpython/opos/boot/01_basic.py
@@ -1,23 +1,7 @@
 import machine
 
 import event
-import value
 
-buf = []
-
-
-@machine.hook_stdin
-def input_handler():
-    while not buf:
-        event.wait(10)
-
-    return int(buf.pop(0))
-
-
-@event.register("key_down")
-def handle_key_down(_0, _1, char, *_):
-    buf.append(char)
 
 
 event.setup()
-value.setup()
diff --git a/src/main/resources/assets/openpython/opos/boot/03_value.py b/src/main/resources/assets/openpython/opos/boot/03_value.py
new file mode 100644
index 00000000..90383030
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/boot/03_value.py
@@ -0,0 +1,3 @@
+import value
+
+value.setup()
diff --git a/src/main/resources/assets/openpython/opos/boot/04_builtin.py b/src/main/resources/assets/openpython/opos/boot/04_builtin.py
index 9338b105..11b7600f 100644
--- a/src/main/resources/assets/openpython/opos/boot/04_builtin.py
+++ b/src/main/resources/assets/openpython/opos/boot/04_builtin.py
@@ -1,6 +1,26 @@
 import builtins
+
+import event
+import machine
 import sys
 
+last_input = []
+buf = []
+
+
+def check_key_down(name, *_):
+    return name == "key_down"
+
+
+@machine.hook_stdin
+def input_handler():
+    while True:
+        signal = event.pull_filtered(1, check_key_down)
+        if signal is not None:
+            _name, _address, char, code, _user = signal
+            if char:
+                return int(char)
+
 
 # noinspection PyShadowingBuiltins
 def input(prompt=None):
diff --git a/src/main/resources/assets/openpython/opos/boot/03_screen.py b/src/main/resources/assets/openpython/opos/boot/05_screen.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/boot/03_screen.py
rename to src/main/resources/assets/openpython/opos/boot/05_screen.py
diff --git a/src/main/resources/assets/openpython/opos/init.py b/src/main/resources/assets/openpython/opos/init.py
index 1d2f2f64..81f3c424 100644
--- a/src/main/resources/assets/openpython/opos/init.py
+++ b/src/main/resources/assets/openpython/opos/init.py
@@ -51,6 +51,9 @@ def print_handler(string):
 def init():
     uos.mount(FileSystem(__path__), '/')
     sys.path.append('/lib')
+    sys.path.append('/lib/internal')
+    sys.path.append('/lib/openos')
+    sys.path.append('/usr/lib')
     sys.path.append('/lib/micropython')
 
     for filename in sorted(uos.listdir("/boot")):
@@ -58,8 +61,8 @@ def init():
         # noinspection PyUnresolvedReferences
         execfile("/boot/" + filename, context)
 
-    from shell import spawn
-    spawn("/bin/python.py")
+    # from shell import spawn
+    # spawn("/bin/python.py")
 
 
 if __name__ == "__main__":
diff --git a/src/main/resources/assets/openpython/opos/lib/computer.py b/src/main/resources/assets/openpython/opos/lib/computer.py
deleted file mode 100644
index 6f434121..00000000
--- a/src/main/resources/assets/openpython/opos/lib/computer.py
+++ /dev/null
@@ -1,2 +0,0 @@
-# noinspection PyUnresolvedReferences
-from ucomputer import *
diff --git a/src/main/resources/assets/openpython/opos/lib/event.py b/src/main/resources/assets/openpython/opos/lib/event.py
deleted file mode 100644
index 7bc41b30..00000000
--- a/src/main/resources/assets/openpython/opos/lib/event.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import machine
-
-from computer import pop_signal
-
-__all__ = ["register", "unregister", "setup"]
-
-event = {}
-lastInterrupt = None
-
-registered = {}
-
-
-def signal_handler(ticks):
-    signal = pop_signal(ticks)
-    if not signal:
-        return
-
-    name, args = signal
-    handlers = registered.get(name)
-    if not handlers:
-        return
-
-    for handler in handlers:
-        try:
-            handler(name, *args)
-        except BaseException as e:
-            machine.debug("signal_handler exc => %s: %s" % (type(e).__name__, e))
-
-
-def listen(name, callback):
-    handlers = registered.setdefault(name, [])
-    if callback in handlers:
-        return False
-
-    handlers.append(callback)
-    return True
-
-
-def ignore(name, callback):
-    handlers = registered.get(name)  # type: list
-    if not handlers:
-        return False
-
-    if callback not in handlers:
-        return False
-
-    handlers.remove(callback)
-    return True
-
-
-def register(name):
-    def wrapper(callback):
-        listen(name, callback)
-        return callback
-
-    return wrapper
-
-
-def unregister(name, func):
-    handlers = registered.setdefault(name, [])
-    handlers.remove(func)
-
-
-def setup():
-    machine.hook_signal(signal_handler)
-
-
-def wait(ticks):
-    signal_handler(ticks)
diff --git a/src/main/resources/assets/openpython/opos/lib/errno.py b/src/main/resources/assets/openpython/opos/lib/internal/errno.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/errno.py
rename to src/main/resources/assets/openpython/opos/lib/internal/errno.py
diff --git a/src/main/resources/assets/openpython/opos/lib/heapq.py b/src/main/resources/assets/openpython/opos/lib/internal/heapq.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/heapq.py
rename to src/main/resources/assets/openpython/opos/lib/internal/heapq.py
diff --git a/src/main/resources/assets/openpython/opos/lib/imp.py b/src/main/resources/assets/openpython/opos/lib/internal/imp.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/imp.py
rename to src/main/resources/assets/openpython/opos/lib/internal/imp.py
diff --git a/src/main/resources/assets/openpython/opos/lib/json.py b/src/main/resources/assets/openpython/opos/lib/internal/json.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/json.py
rename to src/main/resources/assets/openpython/opos/lib/internal/json.py
diff --git a/src/main/resources/assets/openpython/opos/lib/msgpack.py b/src/main/resources/assets/openpython/opos/lib/internal/msgpack.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/msgpack.py
rename to src/main/resources/assets/openpython/opos/lib/internal/msgpack.py
diff --git a/src/main/resources/assets/openpython/opos/lib/random.py b/src/main/resources/assets/openpython/opos/lib/internal/random.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/random.py
rename to src/main/resources/assets/openpython/opos/lib/internal/random.py
diff --git a/src/main/resources/assets/openpython/opos/lib/re.py b/src/main/resources/assets/openpython/opos/lib/internal/re.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/re.py
rename to src/main/resources/assets/openpython/opos/lib/internal/re.py
diff --git a/src/main/resources/assets/openpython/opos/lib/time.py b/src/main/resources/assets/openpython/opos/lib/internal/time.py
similarity index 100%
rename from src/main/resources/assets/openpython/opos/lib/time.py
rename to src/main/resources/assets/openpython/opos/lib/internal/time.py
diff --git a/src/main/resources/assets/openpython/opos/lib/micropython/typing.py b/src/main/resources/assets/openpython/opos/lib/micropython/typing.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/colors.py b/src/main/resources/assets/openpython/opos/lib/openos/colors.py
new file mode 100644
index 00000000..79791b09
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/colors.py
@@ -0,0 +1,44 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/colors.lua
+
+from micropython import const
+
+__all__ = ["WHITE", "ORANGE", "MAGENTA", "LIGHTBLUE",
+           "YELLOW", "LIME", "PINK", "GRAY",
+           "SILVER", "CYAN", "PURPLE", "BLUE",
+           "BROWN", "GREEN", "RED", "BLACK"]
+
+WHITE = const(0)
+ORANGE = const(1)
+MAGENTA = const(2)
+LIGHTBLUE = const(3)
+YELLOW = const(4)
+LIME = const(5)
+PINK = const(6)
+GRAY = const(7)
+SILVER = const(8)
+CYAN = const(9)
+PURPLE = const(10)
+BLUE = const(11)
+BROWN = const(12)
+GREEN = const(13)
+RED = const(14)
+BLACK = const(15)
+
+# alias
+white = WHITE
+orange = ORANGE
+magenta = MAGENTA
+lightblue = LIGHTBLUE
+yellow = YELLOW
+lime = LIME
+pink = PINK
+gray = GRAY
+silver = SILVER
+cyan = CYAN
+purple = PURPLE
+blue = BLUE
+brown = BROWN
+green = GREEN
+red = RED
+black = BLACK
diff --git a/src/main/resources/assets/openpython/opos/lib/component.py b/src/main/resources/assets/openpython/opos/lib/openos/component.py
similarity index 78%
rename from src/main/resources/assets/openpython/opos/lib/component.py
rename to src/main/resources/assets/openpython/opos/lib/openos/component.py
index 623d14ed..b193f4c6 100644
--- a/src/main/resources/assets/openpython/opos/lib/component.py
+++ b/src/main/resources/assets/openpython/opos/lib/openos/component.py
@@ -1,10 +1,11 @@
-from ucomponent import invoke, get_methods, get_doc, get_list, get_type, get_slot
+from ucomponent import invoke, invokes, get_methods, get_doc, get_list, get_type, get_slot
 
 import event
+import sys
 
 _list = list
 
-__all__ = ['Component', 'is_available', 'get_primary', 'get_primary_checked', 'set_primary']
+__all__ = ['Component', 'is_available', 'get_primary', 'get_primary_checked', 'set_primary', 'guard']
 
 primaries = {}
 
@@ -19,6 +20,15 @@ def __init__(self, component, name):
     def __call__(self, *args):
         return invoke(self.component.address, self.name, *args)
 
+    def call(self, *args):
+        return self(*args)  # alias
+
+    def _call(self, *args):
+        return invokes(self.component.address, self.name, *args)
+
+    def _guard_call(self, count, *args):
+        return guard(self._call(*args), count)
+
     @property
     def __doc__(self):
         return doc(self.component.address, self.name)
@@ -128,14 +138,21 @@ def set_primary(component_type: str, address: str):
     primaries[component_type] = proxy(address)
 
 
-@event.register("component_added")
+def guard(result, count: int):
+    if isinstance(result, tuple):
+        return result + (None,) * (count - len(result))
+    else:
+        return (result,) + (None,) * (count - 1)
+
+
+@event.on("component_added")
 def on_component_added(_, address, component_type):
     prev = primaries.get(component_type)
     if prev is None:
         primaries[component_type] = proxy(address)
 
 
-@event.register("component_removed")
+@event.on("component_removed")
 def on_component_removed(_, address, component_type):
     prev = primaries.get(component_type)
     if prev is not None and prev.address == address:
@@ -150,3 +167,12 @@ def setup():
     for address, component_type in get_list().items():
         if not is_available(component_type):
             set_primary(component_type, address)
+
+
+def import_component(component_type: str, module_name: str) -> Component:
+    component = get_primary(component_type)
+    if component is None:
+        del sys.modules[module_name]
+        raise ImportError("component {!r} is missing; import {!r} failed".format(component_type, module_name))
+
+    return component
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/computer.py b/src/main/resources/assets/openpython/opos/lib/openos/computer.py
new file mode 100644
index 00000000..50a29e2d
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/computer.py
@@ -0,0 +1,21 @@
+# noinspection PyUnresolvedReferences
+from ucomputer import *
+import ucomputer
+import utime
+
+
+def address():
+    return ucomputer.get_computer_address()
+
+
+def uptime():
+    return utime.time_up()
+
+
+def pull_signal(seconds):
+    signal = ucomputer.pop_signal(int(seconds * 20))
+    if signal is None:
+        return None
+
+    name, args = signal
+    return (name,) + args
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/devfs.py b/src/main/resources/assets/openpython/opos/lib/openos/devfs.py
new file mode 100644
index 00000000..30e73a04
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/devfs.py
@@ -0,0 +1 @@
+raise NotImplementedError
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/event.py b/src/main/resources/assets/openpython/opos/lib/openos/event.py
new file mode 100644
index 00000000..aad8bf2f
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/event.py
@@ -0,0 +1,259 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/event.lua
+# - src/main/resources/assets/opencomputers/loot/openos/lib/core/full_event.lua
+
+import sys
+
+import computer
+import machine
+import process
+
+from computer import pop_signal
+
+__all__ = ["Handlers", "on", "listen", "pull", "pull_filtered", "pull_multiple", "cancel", "ignore"]
+
+INF = sys.maxsize
+ALWAYS = None
+TIMER = False
+EMPTY = ()
+
+last_interrupt = -1
+
+keyboard = None
+
+
+class Handler:
+    def __init__(self, key, callback, interval: float = None, times=None):
+        self.key = key
+        self.callback = callback
+        self.times = times
+        self.interval = interval  # timer
+        self.timeout = INF if interval is None else computer.uptime() + interval  # timer
+
+
+class Handlers:
+    def __init__(self):
+        self.callback_by_event = {}
+        self.registered = {}
+
+    def register(self, handler: Handler):
+        self.registered[id(handler)] = handler
+
+        seq = self.callback_by_event.setdefault(handler.key, [])
+        seq.append(handler)
+
+        return id(handler)
+
+    def unregister(self, handler: Handler):
+        handler = self.registered.pop(id(handler), None)
+        if handler is not None:
+            key = handler.key
+            seq = self.callback_by_event[key]  # type: list
+            seq.remove(handler)
+            if not seq:
+                del self.callback_by_event[key]
+
+        return handler is None
+
+    def iter_all_handlers(self):
+        return self.registered.values()
+
+    def get_handlers(self, name):
+        return self.callback_by_event.get(name) or EMPTY
+
+    def unregister_by_id(self, timer_id):
+        handler = self.registered.get(timer_id)
+        return self.unregister(handler)
+
+
+handlers = Handlers()
+
+
+def register(key, callback, interval=INF, times=INF, opt_handlers: Handlers = None):
+    opt_handlers = opt_handlers if opt_handlers is not None else handlers
+    handler = Handler(key, callback, interval, times)
+    return opt_handlers.register(handler)
+
+
+def create_plain_filter(name, *args):
+    def func(sname, *sargs):
+        if name != sname:
+            return False
+
+        for arg, sarg in zip(args, sargs):
+            if arg is not None and arg != sarg:
+                return False
+
+        return True
+
+    return func
+
+
+def create_multiple_filter(*args):
+    def func(name, *_):
+        for arg in args:
+            if arg == name:
+                return True
+
+        return False
+
+    return func
+
+
+def signal_handler(ticks):
+    global last_interrupt
+    current_time = computer.uptime()
+    interrupting = (
+            current_time - last_interrupt > 1 and
+            keyboard.is_control_down() and
+            keyboard.is_key_down(keyboard.KEYS.c)
+    ) if keyboard else False
+
+    if interrupting:
+        last_interrupt = current_time
+        if keyboard and keyboard.is_alt_down():
+            process.current_process().signal("INTERRUPTED")
+
+        push("interrupted", current_time)
+
+    removes = set()
+    signal = pop_signal(ticks)
+
+    def process_handler(etype, signal):
+        for handler in handlers.get_handlers(etype):  # type: Handler
+            is_timer = handler.timeout is not None
+            if is_timer and current_time < handler.timeout:
+                continue
+
+            if handler.times is not None:
+                handler.times -= 1
+                if handler.times <= 0:
+                    removes.add(handler)
+                elif handler.interval is not None:
+                    handler.timeout = current_time + handler.interval
+
+            try:
+                name, args = signal
+                result = handler.callback(name, *args)
+            except BaseException as e:
+                on_error(e)
+            else:
+                if result is False:
+                    removes.add(handler)
+
+    process_handler(TIMER, None)
+    for handler in removes:
+        handlers.unregister(handler)
+
+    if signal is None:
+        return
+
+    removes = set()
+    name, args = signal
+    process_handler(ALWAYS, signal)
+    process_handler(name, signal)
+
+    for handler in removes:
+        handlers.unregister(handler)
+
+    return (name,) + args
+
+
+def on(name):
+    def wrapper(callback):
+        listen(name, callback)
+        return callback
+
+    return wrapper
+
+
+def listen(name, callback):
+    for handler in handlers.registered.values():
+        if handler.key == name and handler.callback == callback:
+            return False
+
+    return register(name, callback)
+
+
+def ignore(name, callback):
+    removes = set()
+    for handler in handlers.iter_all_handlers():
+        if handler.key == name and handler.callback == callback:
+            removes.add(handler)
+            break  # ..?
+
+    for handler in removes:
+        handlers.unregister(handler)
+
+    return len(removes) > 0
+
+
+def timer(interval: float, callback, times: int):
+    return register(TIMER, callback, interval, times)
+
+
+def cancel(timer_id):
+    return handlers.unregister_by_id(timer_id)
+
+
+def push(name, *args):
+    computer.push_signal(name, *args)
+
+
+def pull(first, *args):
+    if isinstance(first, str):
+        return pull_filtered(create_plain_filter(first, *args))
+    else:
+        return pull_filtered(first, create_plain_filter(*args))
+
+
+def pull_filtered(first, second=None):
+    seconds = INF
+    func = None
+
+    if callable(first) and second is None:
+        func = first
+    elif callable(second):
+        seconds = first
+        func = second
+
+    deadline = computer.uptime() + seconds
+    while computer.uptime() < deadline:
+        closest = deadline
+        for handler in handlers.iter_all_handlers():
+            closest = min(closest, handler.timeout)
+
+        signal = computer.pull_signal(closest - computer.uptime())
+        if signal is None:
+            continue
+
+        if func(*signal):
+            return signal
+
+
+def pull_multiple(first, *args):
+    if isinstance(first, int):
+        return pull_filtered(first, create_multiple_filter(*args))
+    else:
+        return pull_filtered(create_multiple_filter(first, *args))
+
+
+def on_error(e):
+    machine.debug("signal_handler exc => %s: %s" % (type(e).__name__, e))
+
+
+def setup():
+    global keyboard
+    def pull_signal(seconds):
+        ticks = sys.maxsize if seconds == INF else int(seconds * 20)
+        return signal_handler(ticks)
+
+    # noinspection PyUnresolvedReferences
+    import keyboard
+
+    computer.pull_signal = pull_signal
+    machine.hook_signal(signal_handler)
+
+
+def wait(ticks):
+    signal_handler(ticks)
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/internet.py b/src/main/resources/assets/openpython/opos/lib/openos/internet.py
new file mode 100644
index 00000000..54a4d464
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/internet.py
@@ -0,0 +1,32 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# src/main/resources/assets/opencomputers/loot/openos/lib/internet.lua
+
+import component
+
+robot = component.import_component("robot", __name__)
+
+
+def request(url: str, data=None, headers: dict = None):
+    internet = component.get_primary("internet")
+
+    post = None
+    if data is None:
+        pass
+    elif isinstance(data, (str, bytes)):
+        post = data
+    elif isinstance(data, dict):
+        post = "&".join("{}={}".format(key, value) for key, value in data.items())
+    else:
+        raise TypeError
+
+    return internet.request(url, post, headers)
+
+
+def socket(address, port):
+    internet = component.get_primary("internet")
+    return internet.socket(address, port)
+
+
+def open(address, port):
+    internet = component.get_primary("internet")
+    return internet.open(address, port)
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/keyboard.py b/src/main/resources/assets/openpython/opos/lib/openos/keyboard.py
new file mode 100644
index 00000000..00e0d715
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/keyboard.py
@@ -0,0 +1,236 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/keyboard.lua
+# - src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua
+
+import component
+from micropython import const
+
+keyboards_pressed_chars = {}
+keyboards_pressed_codes = {}
+
+__all__ = ["get_keyboard_address", "get_pressed_codes", "get_pressed_chars",
+           "is_alt_down", "is_control", "is_control_down", "is_key_down", "is_shift_down"]
+
+_LMENU = const(0x38)
+_RMENU = const(0xB8)
+_LCONTROL = const(0x1D)
+_RCONTROL = const(0x9D)
+_LSHIFT = const(0x2A)
+_RSHIFT = const(0x36)
+
+
+class KEYS:
+    N1 = 0x02
+    N2 = 0x03
+    N3 = 0x04
+    N4 = 0x05
+    N5 = 0x06
+    N6 = 0x07
+    N7 = 0x08
+    N8 = 0x09
+    N9 = 0x0A
+    N0 = 0x0B
+    A = 0x1E
+    B = 0x30
+    C = 0x2E
+    D = 0x20
+    E = 0x12
+    F = 0x21
+    G = 0x22
+    H = 0x23
+    I = 0x17
+    J = 0x24
+    K = 0x25
+    L = 0x26
+    M = 0x32
+    N = 0x31
+    O = 0x18
+    P = 0x19
+    Q = 0x10
+    R = 0x13
+    S = 0x1F
+    T = 0x14
+    U = 0x16
+    V = 0x2F
+    W = 0x11
+    X = 0x2D
+    Y = 0x15
+    Z = 0x2C
+
+    APOSTROPHE = 0x28
+    AT = 0x91
+    BACK = 0x0E  # BACKSPACE
+    BACKSLASH = 0x2B
+    CAPITAL = 0x3A  # CAPSLOCK
+    COLON = 0x92
+    COMMA = 0x33
+    ENTER = 0x1C
+    EQUALS = 0x0D
+    GRAVE = 0x29  # ACCENT GRAVE
+    LBRACKET = 0x1A
+    LCONTROL = 0x1D
+    LMENU = 0x38  # LEFT ALT
+    LSHIFT = 0x2A
+    MINUS = 0x0C
+    NUMLOCK = 0x45
+    PAUSE = 0xC5
+    PERIOD = 0x34
+    RBRACKET = 0x1B
+    RCONTROL = 0x9D
+    RMENU = 0xB8  # RIGHT ALT
+    RSHIFT = 0x36
+    SCROLL = 0x46  # SCROLL LOCK
+    SEMICOLON = 0x27
+    SLASH = 0x35  # / ON MAIN KEYBOARD
+    SPACE = 0x39
+    STOP = 0x95
+    TAB = 0x0F
+    UNDERLINE = 0x93
+
+    # KEYPAD (AND NUMPAD WITH NUMLOCK OFF)
+    UP = 0xC8
+    DOWN = 0xD0
+    LEFT = 0xCB
+    RIGHT = 0xCD
+    HOME = 0xC7
+    END = 0xCF
+    PAGEUP = 0xC9
+    PAGEDOWN = 0xD1
+    INSERT = 0xD2
+    DELETE = 0xD3
+
+    # FUNCTION KEYS
+    F1 = 0x3B
+    F2 = 0x3C
+    F3 = 0x3D
+    F4 = 0x3E
+    F5 = 0x3F
+    F6 = 0x40
+    F7 = 0x41
+    F8 = 0x42
+    F9 = 0x43
+    F10 = 0x44
+    F11 = 0x57
+    F12 = 0x58
+    F13 = 0x64
+    F14 = 0x65
+    F15 = 0x66
+    F16 = 0x67
+    F17 = 0x68
+    F18 = 0x69
+    F19 = 0x71
+
+    # JAPANESE KEYBOARDS
+    KANA = 0x70
+    KANJI = 0x94
+    CONVERT = 0x79
+    NOCONVERT = 0x7B
+    YEN = 0x7D
+    CIRCUMFLEX = 0x90
+    AX = 0x96
+
+    # NUMPAD
+    NUMPAD0 = 0x52
+    NUMPAD1 = 0x4F
+    NUMPAD2 = 0x50
+    NUMPAD3 = 0x51
+    NUMPAD4 = 0x4B
+    NUMPAD5 = 0x4C
+    NUMPAD6 = 0x4D
+    NUMPAD7 = 0x47
+    NUMPAD8 = 0x48
+    NUMPAD9 = 0x49
+    NUMPADMUL = 0x37
+    NUMPADDIV = 0xB5
+    NUMPADSUB = 0x4A
+    NUMPADADD = 0x4E
+    NUMPADDECIMAL = 0x53
+    NUMPADCOMMA = 0xB3
+    NUMPADENTER = 0x9C
+    NUMPADEQUALS = 0x8D
+
+    def __init__(self):
+        index = {}
+        for name in dir(self):
+            value = getattr(self, name)
+            if isinstance(value, int):
+                if name.startswith("N") and len(name) == 2:
+                    index[name[1:]] = value
+                    index[name] = value
+                    index[value] = name
+                elif len(name) == 1:
+                    index[name.upper()] = value
+                    index[name.lower()] = value
+                    index[value] = name
+                else:
+                    index[name] = value
+                    index[value] = name
+
+        self._index = index
+
+    def __getitem__(self, item):
+        return self._index[item]
+
+
+KEYS = KEYS()
+
+assert _LMENU == KEYS.LMENU
+assert _RMENU == KEYS.RMENU
+assert _LCONTROL == KEYS.LCONTROL
+assert _RCONTROL == KEYS.RCONTROL
+assert _LSHIFT == KEYS.LSHIFT
+assert _RSHIFT == KEYS.RSHIFT
+
+
+def get_keyboard_address(address=None):
+    if address is not None:
+        return address
+
+    keyboard = component.get_primary("keyboard")
+    if keyboard is not None:
+        return keyboard.address
+
+    return None
+
+
+def get_pressed_codes(address=None):
+    address = get_keyboard_address(address)
+    return keyboards_pressed_codes.get(address) if address else None
+
+
+def get_pressed_chars(address=None):
+    address = get_keyboard_address(address)
+    return keyboards_pressed_chars.get(address) if address else None
+
+
+def is_alt_down(address=None) -> bool:
+    pressed_codes = get_pressed_codes(address)
+    return (pressed_codes.get(_LMENU) or pressed_codes.get(_RMENU)) \
+        if pressed_codes is not None else False
+
+
+def is_control(char: int) -> bool:
+    return ((char < 0x20) or (0x7F <= char <= 0x9F)) if isinstance(char, int) else False
+
+
+def is_control_down(address=None) -> bool:
+    pressed_codes = get_pressed_codes(address)
+    return pressed_codes.get(_LCONTROL) or pressed_codes.get(_RCONTROL) \
+        if pressed_codes is not None else False
+
+
+def is_key_down(char_or_code, address=None) -> bool:
+    if isinstance(char_or_code, str):
+        pressed_chars = get_pressed_chars(address)
+        return bool(pressed_chars.get(char_or_code)) \
+            if pressed_chars is not None else False
+    else:
+        pressed_codes = get_pressed_codes(address)
+        return bool(pressed_codes.get(char_or_code)) \
+            if pressed_codes is not None else False
+
+
+def is_shift_down(address=None):
+    pressed_codes = get_pressed_codes(address)
+    return pressed_codes.get(_LSHIFT) or pressed_codes.get(_RSHIFT) \
+        if pressed_codes is not None else False
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/note.py b/src/main/resources/assets/openpython/opos/lib/openos/note.py
new file mode 100644
index 00000000..4d3c64c3
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/note.py
@@ -0,0 +1,24 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/note.lua
+
+note = {}
+notes = {}  # The table that maps note names to their respective MIDI codes
+reverseNotes = {}  # The reversed table "notes"
+
+tempNotes = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
+sNotes = ["a0", "a#0", "b0"]
+bNotes = ["bb0"]
+
+for i in range(1, 6 + 1):
+    for v in tempNotes:
+        sNotes.append(v + str(i))
+        if len(v) == 1 and v != "c" and v != "f":
+            bNotes.append(v + "b" + str(i))
+
+for v in range(21, 95 + 1):
+    k = sNotes[v - 20 - 1]
+    notes[k] = str(v)
+    reverseNotes[v] = k
+
+# TODO: ?
+raise NotImplementedError
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/pipe.py b/src/main/resources/assets/openpython/opos/lib/openos/pipe.py
new file mode 100644
index 00000000..da847436
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/pipe.py
@@ -0,0 +1,4 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/pipe.lua
+
+raise NotImplementedError
diff --git a/src/main/resources/assets/openpython/opos/lib/process.py b/src/main/resources/assets/openpython/opos/lib/openos/process.py
similarity index 52%
rename from src/main/resources/assets/openpython/opos/lib/process.py
rename to src/main/resources/assets/openpython/opos/lib/openos/process.py
index 1c8e43b1..42e8860e 100644
--- a/src/main/resources/assets/openpython/opos/lib/process.py
+++ b/src/main/resources/assets/openpython/opos/lib/openos/process.py
@@ -1,3 +1,6 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/process.lua
+
 try:
     import usys as sys
 except ImportError:
@@ -12,13 +15,26 @@ def __init__(self, context):
         self.context = context
 
 
+_current_proccess = Process({})
+
+
 def spawn(path):
+    global _current_proccess
     context = {'__name__': '__main__', '__path__': path}
 
+    proc = Process({})
+    parent_proc, _current_proccess = _current_proccess, proc
+
     try:
         # noinspection PyUnresolvedReferences
         execfile(path, context)
     except SystemExit as e:
         return e.code
+    finally:
+        _current_proccess = parent_proc
 
     return 0
+
+
+def current_process():
+    return _current_proccess
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/rc.py b/src/main/resources/assets/openpython/opos/lib/openos/rc.py
new file mode 100644
index 00000000..79658ca1
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/rc.py
@@ -0,0 +1,4 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/rc.lua
+
+loaded = {}
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/robot.py b/src/main/resources/assets/openpython/opos/lib/openos/robot.py
new file mode 100644
index 00000000..e51cac56
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/robot.py
@@ -0,0 +1,340 @@
+from micropython import const
+
+import component
+import random
+from sides import *
+
+robot = component.import_component("robot", __name__)
+
+__all__ = [
+    # General
+    "name", "get_light_color", "set_light_color",
+
+    # World
+    "detect",
+
+    # Inventory
+    "slot", "compare", "drop", "place", "suck",
+
+    # Tool
+    "durability", "swing", "use",
+
+    # Movement
+    "move", "forward", "back", "up", "down", "turn",
+
+    # Tank
+    "tank", "compare_fluid", "drain", "fill",
+]
+
+# check sides.py
+DOWN = const(0)
+UP = const(1)
+BACK = const(2)
+FRONT = const(3)
+
+
+class SidedMethod:
+    def __init__(self, func):
+        self.func = func
+
+    def __doc__(self):
+        return self.func.__doc__
+
+    # __str__, __repr__ later
+
+    def __call__(self, *args):
+        return self.front(*args)
+
+    def front(self, *args):
+        return self.func(FRONT, *args)
+
+    def up(self, *args):
+        return self.func(UP, *args)
+
+    def down(self, *args):
+        return self.func(DOWN, *args)
+
+
+class SidedContextMethod:
+    def __init__(self, func, context_func):
+        self.func = func
+        self.context_func = context_func
+
+    def __doc__(self):
+        return self.func.__doc__
+
+    # __str__, __repr__ later
+
+    def __call__(self, *args):
+        return self.front(*args)
+
+    def front(self, *args):
+        with self.context_func():
+            return self.func(FRONT, *args)
+
+    def up(self, *args):
+        with self.context_func():
+            return self.func(UP, *args)
+
+    def down(self, *args):
+        with self.context_func():
+            return self.func(DOWN, *args)
+
+
+
+
+class RobotResult:
+    def __init__(self, result):
+        self.result = result
+
+    @property
+    def success(self):
+        return self.result[0]
+
+    @property
+    def reason(self):
+        return self.result[1] if len(self.result) >= 2 else None
+
+    def unwarp(self):
+        if not self:
+            raise Exception(self.reason)
+
+        return True
+
+    def __bool__(self):
+        return bool(self.success)
+
+    @classmethod
+    def proxy(cls, method):
+        # noinspection PyProtectedMember
+        func = method._call
+
+        return lambda *args: cls(func(*args))
+
+    def __repr__(self):
+        if self:
+            return "<Success>"
+        else:
+            if self.reason is None:
+                return "<Failure>"
+            else:
+                return "<Failure: {!r}>".format(self.reason)
+
+
+def all_slots():
+    pass
+
+
+# General
+def name():
+    return robot.name()
+
+
+def get_light_color() -> int:
+    return robot.getLightColor()
+
+
+def set_light_color(color: int):
+    return robot.setLightColor(color)
+
+
+# World
+detect = SidedMethod(robot.detect)
+
+
+# Inventory
+class SlotContext:
+    def __init__(self, slot):
+        self.slot = slot
+        self.selected = None
+
+    def __enter__(self):
+        self.selected = robot.select()
+        robot.select(self.slot)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        robot.select(self.slot)
+
+
+class Slot:
+    @classmethod
+    def selected(cls):
+        return robot.select()
+
+    @classmethod
+    def size(cls):
+        return robot.inventorySize()
+
+    def __init__(self, slot):
+        self.slot = slot
+
+    def __int__(self):
+        return self.slot
+
+    def select(self):
+        success = robot.select(self.slot) == self.slot
+        if success:
+            _SELECTED_SLOT = self.slot
+
+        return success
+
+    @property
+    def count(self):
+        return robot.count(self.slot)
+
+    @property
+    def space(self):
+        return robot.space(self.slot)
+
+    def context(self):
+        return SlotContext(self.slot)
+
+    def compare_to(self, another_slot):
+        with self.context():
+            return robot.compareTo(another_slot)
+
+    def transfer(self, new_slot, count: int):
+        with self.context():
+            return robot.transferTo(new_slot, count)
+
+    @property
+    def compare(self):
+        return SidedContextMethod(robot.compare, self.context)
+
+    @property
+    def drop(self):
+        return SidedContextMethod(robot.drop, self.context)
+
+    @property
+    def place(self):
+        return SidedContextMethod(robot.place, self.context)
+
+    @property
+    def suck(self):
+        return SidedContextMethod(robot.suck, self.context)
+
+
+slot = Slot
+
+# Inventory + World
+compare = SidedMethod(robot.compare)
+drop = SidedMethod(robot.drop)
+place = SidedMethod(RobotResult.proxy(robot.place))
+suck = SidedMethod(robot.suck)
+
+# Tool
+durability = robot.durability
+swing = SidedMethod(RobotResult.proxy(robot.swing))
+use = SidedMethod(RobotResult.proxy(robot.use))
+
+
+# Movement
+
+
+class Move:
+    def __init__(self):
+        self._move = RobotResult.proxy(robot.move)
+
+    def forward(self):
+        return self._move(FRONT)
+
+    def back(self):
+        return self._move(BACK)
+
+    def up(self):
+        return self._move(UP)
+
+    def down(self):
+        return self._move(DOWN)
+
+
+move = Move()
+forward = move.forward
+back = move.back
+up = move.up
+down = move.down
+
+
+class Turn:
+    def __init__(self):
+        self._turn = RobotResult.proxy(robot.turn)
+
+    def left(self):
+        return self._turn(False)
+
+    def right(self):
+        return self._turn(True)
+
+    def around(self):
+        turn = random.random() > 0.5
+        return self._turn(turn) and self._turn(turn)
+
+
+turn = Turn()
+
+
+# Tank
+class TankContext:
+    def __init__(self, tank):
+        self.tank = tank
+        self.selected = None
+
+    def __enter__(self):
+        self.selected = robot.selectTank()
+        robot.selectTank(self.tank)
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        robot.selectTank(self.tank)
+
+
+class Tank:
+    @classmethod
+    def selected(cls):
+        return robot.selectTank()
+
+    def __init__(self, slot):
+        self.tank = slot
+
+    def __int__(self):
+        return self.tank
+
+    def select(self):
+        return robot.selectTank(self.tank) == self.tank
+
+    @property
+    def level(self):
+        return robot.tankLevel(self.tank)
+
+    @property
+    def space(self):
+        return robot.tankSpace(self.tank)
+
+    def context(self):
+        return TankContext(self.tank)
+
+    def compare_to(self, another_tank):
+        with self.context():
+            return robot.compareFluidTo(another_tank)
+
+    def transfer(self, new_tank, count: int):
+        with self.context():
+            return robot.transferFluidTo(new_tank, count)
+
+    @property
+    def compare(self):
+        return SidedContextMethod(robot.compareFluid, self.context)
+
+    @property
+    def drain(self):
+        return SidedContextMethod(robot.drain, self.context)
+
+    @property
+    def fill(self):
+        return SidedContextMethod(robot.fill, self.context)
+
+
+tank = Tank
+
+compare_fluid = SidedMethod(robot.compareFluid)
+drain = SidedMethod(robot.drain)
+fill = SidedMethod(robot.fill)
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/serialization.py b/src/main/resources/assets/openpython/opos/lib/openos/serialization.py
new file mode 100644
index 00000000..36c200ff
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/serialization.py
@@ -0,0 +1,15 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/serialization.lua
+
+__all__ = ["serialize", "unserialize"]
+
+
+def serialize(value, pretty=False):
+    raise NotImplementedError
+
+
+def unserialize(value):
+    raise NotImplementedError
+
+
+raise NotImplementedError
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/shell.py b/src/main/resources/assets/openpython/opos/lib/openos/shell.py
new file mode 100644
index 00000000..b0b4b7ec
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/shell.py
@@ -0,0 +1,8 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/shell.lua
+# - src/main/resources/assets/opencomputers/loot/openos/lib/core/full_shell.lua
+
+
+from process import spawn
+
+__all__ = ["spawn"]
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/sides.py b/src/main/resources/assets/openpython/opos/lib/openos/sides.py
new file mode 100644
index 00000000..52637b6d
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/sides.py
@@ -0,0 +1,29 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/sides.lua
+
+from micropython import const
+
+__all__ = ["BOTTOM", "TOP", "BACK", "FRONT", "RIGHT", "LEFT"]
+
+BOTTOM = const(0)
+TOP = const(1)
+BACK = const(2)
+FRONT = const(3)
+RIGHT = const(4)
+LEFT = const(5)
+
+# alias
+NEGY = DOWN = BOTTOM
+POSY = UP = TOP
+NEGZ = NORTH = BACK
+POSZ = SOUTH = FORWARD = FRONT
+NEGX = WEST = RIGHT
+POSX = EAST = LEFT
+
+# alias for lowercase
+negy = down = bottom = BOTTOM
+posy = up = top = TOP
+negz = north = back = BACK
+posz = south = forward = front = FRONT
+negx = west = right = RIGHT
+posx = east = left = LEFT
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/term.py b/src/main/resources/assets/openpython/opos/lib/openos/term.py
new file mode 100644
index 00000000..5826e715
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/term.py
@@ -0,0 +1,2 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/term.lua
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/text.py b/src/main/resources/assets/openpython/opos/lib/openos/text.py
new file mode 100644
index 00000000..d9e33f05
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/text.py
@@ -0,0 +1,2 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/text.lua
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/tty.py b/src/main/resources/assets/openpython/opos/lib/openos/tty.py
new file mode 100644
index 00000000..cc7fe9ba
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/tty.py
@@ -0,0 +1,61 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/tty.lua
+
+import event
+
+__all__ = "window"
+
+screen_cache = {}
+gpu_intercept = {}
+
+
+class Window:
+    def __init__(self):
+        self.fullscreen = True
+        self.blink = True
+        self.dx = 0
+        self.dy = 0
+        self.x = 0
+        self.y = 1
+        self.width = None
+        self.height = None
+        self.output_buffer = ""
+        self.stream = None
+        self.gpu = None
+
+    @property
+    def screen(self):
+        return self.gpu.getScreen() if self.gpu else None
+
+    def get_viewport(self):
+        screen = self.screen
+        if self.fullscreen and screen and not screen_cache[screen]:
+            screen_cache[screen] = True
+            self.height, self.height = self.gpu.getViewPort()
+
+        return self.width, self.height, self.dx, self.dy, self.x, self.y
+
+    def clear(self):
+        self.stream.scroll(None)
+        self.set_cursor(1, 1)
+
+    def is_available(self):
+        return bool(self.gpu and self.screen)
+
+    def bind(self, gpu):
+
+
+class WindowStream:
+    def read(self):
+        pass
+
+    def write(self, value):
+        pass
+
+
+window = Window()
+
+
+@event.on("screen_resized")
+def screen_reset(_name, gpu, addr):
+    pass
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/uuid.py b/src/main/resources/assets/openpython/opos/lib/openos/uuid.py
new file mode 100644
index 00000000..282a4c93
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/uuid.py
@@ -0,0 +1,2 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/uuid.lua
diff --git a/src/main/resources/assets/openpython/opos/lib/openos/vt100.py b/src/main/resources/assets/openpython/opos/lib/openos/vt100.py
new file mode 100644
index 00000000..6c8dec04
--- /dev/null
+++ b/src/main/resources/assets/openpython/opos/lib/openos/vt100.py
@@ -0,0 +1,2 @@
+# https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/
+# - src/main/resources/assets/opencomputers/loot/openos/lib/vt100.lua
diff --git a/src/main/resources/assets/openpython/opos/lib/shell.py b/src/main/resources/assets/openpython/opos/lib/shell.py
deleted file mode 100644
index 1825b89a..00000000
--- a/src/main/resources/assets/openpython/opos/lib/shell.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from process import spawn
-
-__all__ = ["spawn"]
diff --git a/src/main/resources/assets/openpython/opos/lib/urllib/parse.py b/src/main/resources/assets/openpython/opos/lib/urllib/parse.py
index 8e65d2bf..17734bb4 100644
--- a/src/main/resources/assets/openpython/opos/lib/urllib/parse.py
+++ b/src/main/resources/assets/openpython/opos/lib/urllib/parse.py
@@ -28,7 +28,6 @@
 """
 
 import re
-import sys
 import collections
 
 __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag",
@@ -890,7 +889,7 @@ def splitpasswd(user):
     global _passwdprog
     if _passwdprog is None:
         import re
-        _passwdprog = re.compile('^([^:]*):(.*)$',re.S)
+        _passwdprog = re.compile('^([^:]*):(.*)$', re.S)
 
     match = _passwdprog.match(user)
     if match: return match.group(1, 2)