portacode 1.4.13.dev1__tar.gz → 1.4.13.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/PKG-INFO +1 -1
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/_version.py +2 -2
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +6 -2
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/base.py +15 -45
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/proxmox_infra.py +161 -118
- portacode-1.4.13.dev3/portacode/connection/handlers/test_proxmox_infra.py +13 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/SOURCES.txt +1 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/.claude/agents/communication-manager.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/.claude/settings.local.json +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/.gitignore +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/.gitmodules +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/LICENSE +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/MANIFEST.in +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/Makefile +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/backup.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/connect.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/connect.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docker-compose.yaml +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docs/images/device-transfer-button.png +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docs/images/device-transfer-modal.png +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docs/images/pair-device-button.png +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docs/images/pairing-request.png +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/docs/images/student-workspace.png +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/simple_device/Dockerfile +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/simple_device/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/simple_device/docker-compose.yaml +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/Dockerfile +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/docker-compose.yaml +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/manage.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/__main__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/cli.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/client.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/chunked_content.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/diff_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/git_manager.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/manager.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state/utils.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/registry.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/session.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/update_handler.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/multiplex.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/terminal.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/data.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/keypair.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/elinks +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/gio-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/gnome-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/gvfs-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/kde-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/kfmclient +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/links +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/links2 +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/lynx +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/mate-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/netsurf +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/sensible-browser +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/w3m +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/x-www-browser +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/link_capture/bin/xdg-open +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/logging_categories.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/pairing.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/service.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/static/js/test-ntp-clock.html +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/static/js/utils/ntp-clock.js +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/utils/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/utils/diff_apply.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/utils/diff_renderer.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/utils/ntp_clock.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/requires.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode.egg-info/top_level.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/pyproject.toml +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/restore.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/run_tests.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/setup.cfg +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/setup.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_device_online.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_file_operations.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_git_status_ui.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_login_flow.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_play_store_screenshots.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_terminal_buffer_performance.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_terminal_loading_race_condition.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_modules/test_terminal_start.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/test_request_id.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/.env.example +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/README.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/cli.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/__init__.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/base_test.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/cli_manager.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/runner.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/core/test_discovery.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/testing_framework/requirements.txt +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/agent_context_management.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/django_server_time_sync.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/device_performance_degradation.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/indefinite_resource_loading.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/portacode_service_silently_down.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/premature_terminal_exit.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/project_cpu_hotspots.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/terminals_exit_upon_starting.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/todo/smartphone_terminal_input_frustrations.md +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/tools/generate_play_store_assets.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/tools/pairing_tester.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/tools/run_screenshot_suite.sh +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/tools/test_python_ntp_clock.py +0 -0
- {portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/validate.sh +0 -0
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '1.4.13.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 13, '
|
|
31
|
+
__version__ = version = '1.4.13.dev3'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 13, 'dev3')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
{portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
@@ -27,8 +27,6 @@ The Portacode server acts as a **routing middleman** between client sessions and
|
|
|
27
27
|
|
|
28
28
|
- **`source_client_session`** (Server → Device): Server **adds this** when forwarding client commands to devices (so device knows which client sent the command and can target responses back). Clients never include this field.
|
|
29
29
|
|
|
30
|
-
Device handlers that want to respond only to the session that issued a given command can rely on the helper defined in `portacode/connection/handlers/base.py` (`BaseHandler.send_response_to_source_session`). It wraps the boilerplate of reusing `request_id`/`trace`, injecting `client_sessions=[source_client_session]`, and sending via the control channel so the handler does not need to manually reconstruct routing metadata for each reply.
|
|
31
|
-
|
|
32
30
|
This document describes the complete protocol for communicating with devices through the server, guiding app developers on how to get their client sessions to communicate with devices.
|
|
33
31
|
|
|
34
32
|
## Table of Contents
|
|
@@ -363,6 +361,9 @@ Creates a Portacode-managed LXC container, starts it, and bootstraps the Portaco
|
|
|
363
361
|
* `username` (string, optional): OS user to provision (defaults to `svcuser`).
|
|
364
362
|
* `password` (string, optional): Password for the user (used only during provisioning).
|
|
365
363
|
* `ssh_key` (string, optional): SSH public key to add to the user.
|
|
364
|
+
* `device_id` (string, optional): ID of the Device record that already exists on the dashboard.
|
|
365
|
+
* `device_public_key` (string, optional): PEM-encoded Portacode public key. When supplied together with `device_private_key` the handler injects the keypair, records the device metadata, and runs `portacode service install` automatically.
|
|
366
|
+
* `device_private_key` (string, optional): PEM-encoded private key that pairs with `device_public_key`. Both key fields must be present for the automatic service-install mode.
|
|
366
367
|
|
|
367
368
|
**Responses:**
|
|
368
369
|
|
|
@@ -420,6 +421,8 @@ Emitted after a successful `create_proxmox_container` action. Contains the new c
|
|
|
420
421
|
* `public_key` (string): Portacode public auth key created inside the new container.
|
|
421
422
|
* `container` (object): Metadata such as `vmid`, `hostname`, `template`, `storage`, `disk_gib`, `ram_mib`, and `cpus`.
|
|
422
423
|
* `setup_steps` (array[object]): Detailed bootstrap step results (name, stdout/stderr, elapsed time, and status).
|
|
424
|
+
* `device_id` (string, optional): Mirrors the `device_id` supplied with `create_proxmox_container`, if any.
|
|
425
|
+
* `service_installed` (boolean): True when the handler already ran `portacode service install` (with a provided keypair); otherwise it remains False and the dashboard can call `start_portacode_service`.
|
|
423
426
|
|
|
424
427
|
### `proxmox_container_progress`
|
|
425
428
|
|
|
@@ -452,6 +455,7 @@ Runs `sudo portacode service install` inside the container after the dashboard h
|
|
|
452
455
|
* Emits additional [`proxmox_container_progress`](#proxmox_container_progress-event) events to report the authentication and service-install steps.
|
|
453
456
|
* On success, emits a [`proxmox_service_started`](#proxmox_service_started-event).
|
|
454
457
|
* On failure, emits a generic [`error`](#error) event.
|
|
458
|
+
* When `create_proxmox_container` already provided a dashboard-generated keypair, the handler may have installed the service already, so this call is optional unless you need to re-run the install.
|
|
455
459
|
|
|
456
460
|
### `proxmox_service_started`
|
|
457
461
|
|
|
@@ -88,42 +88,6 @@ class BaseHandler(ABC):
|
|
|
88
88
|
if reply_channel:
|
|
89
89
|
payload["reply_channel"] = reply_channel
|
|
90
90
|
await self.control_channel.send(payload)
|
|
91
|
-
|
|
92
|
-
async def send_response_to_source_session(
|
|
93
|
-
self,
|
|
94
|
-
message: Dict[str, Any],
|
|
95
|
-
payload: Dict[str, Any],
|
|
96
|
-
reply_channel: Optional[str] = None,
|
|
97
|
-
) -> None:
|
|
98
|
-
"""Send a response directly to the client session that issued ``message``.
|
|
99
|
-
|
|
100
|
-
This bypasses ``ClientSessionManager`` routing and ensures only the session
|
|
101
|
-
whose ``source_client_session`` was injected by the server will receive the
|
|
102
|
-
response. The helper also copies ``request_id``/``trace`` timing data so tracing
|
|
103
|
-
and logging remain consistent.
|
|
104
|
-
"""
|
|
105
|
-
response = dict(payload)
|
|
106
|
-
request_id = message.get("request_id")
|
|
107
|
-
if request_id and "request_id" not in response:
|
|
108
|
-
response["request_id"] = request_id
|
|
109
|
-
|
|
110
|
-
if "trace" in message and "request_id" in message and "trace" not in response:
|
|
111
|
-
trace_data = dict(message["trace"])
|
|
112
|
-
handler_complete_time = ntp_clock.now_ms()
|
|
113
|
-
if handler_complete_time is not None:
|
|
114
|
-
trace_data["handler_complete"] = handler_complete_time
|
|
115
|
-
if "client_send" in trace_data:
|
|
116
|
-
trace_data["ping"] = handler_complete_time - trace_data["client_send"]
|
|
117
|
-
response["trace"] = trace_data
|
|
118
|
-
|
|
119
|
-
source_client_session = message.get("source_client_session")
|
|
120
|
-
if source_client_session:
|
|
121
|
-
response["client_sessions"] = [source_client_session]
|
|
122
|
-
|
|
123
|
-
if reply_channel:
|
|
124
|
-
response["reply_channel"] = reply_channel
|
|
125
|
-
|
|
126
|
-
await self.control_channel.send(response)
|
|
127
91
|
|
|
128
92
|
async def send_error(self, message: str, reply_channel: Optional[str] = None, project_id: str = None) -> None:
|
|
129
93
|
"""Send an error response with client session awareness.
|
|
@@ -177,16 +141,22 @@ class AsyncHandler(BaseHandler):
|
|
|
177
141
|
if "request_id" in message and "request_id" not in response:
|
|
178
142
|
response["request_id"] = message["request_id"]
|
|
179
143
|
|
|
180
|
-
|
|
181
|
-
|
|
144
|
+
# Pass through trace from request to response (add to existing trace, don't create new one)
|
|
145
|
+
if "trace" in message and "request_id" in message:
|
|
146
|
+
response["trace"] = dict(message["trace"])
|
|
182
147
|
handler_complete_time = ntp_clock.now_ms()
|
|
183
148
|
if handler_complete_time is not None:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
149
|
+
response["trace"]["handler_complete"] = handler_complete_time
|
|
150
|
+
# Update ping to show total time from client_send
|
|
151
|
+
if "client_send" in response["trace"]:
|
|
152
|
+
response["trace"]["ping"] = handler_complete_time - response["trace"]["client_send"]
|
|
153
|
+
logger.info(f"✅ Handler completed: {message['request_id']} ({self.command_name})")
|
|
154
|
+
|
|
155
|
+
# Extract project_id from response for session targeting
|
|
156
|
+
project_id = response.get("project_id")
|
|
157
|
+
logger.info("handler: %s response project_id=%s, response=%s",
|
|
158
|
+
self.command_name, project_id, response)
|
|
159
|
+
await self.send_response(response, reply_channel, project_id)
|
|
190
160
|
else:
|
|
191
161
|
logger.info("handler: %s handled response transmission directly", self.command_name)
|
|
192
162
|
except Exception as exc:
|
|
@@ -246,4 +216,4 @@ class SyncHandler(BaseHandler):
|
|
|
246
216
|
logger.exception("Error in sync handler %s: %s", self.command_name, exc)
|
|
247
217
|
# Extract project_id from original message for error targeting
|
|
248
218
|
project_id = message.get("project_id")
|
|
249
|
-
await self.send_error(str(exc), reply_channel, project_id)
|
|
219
|
+
await self.send_error(str(exc), reply_channel, project_id)
|
{portacode-1.4.13.dev1 → portacode-1.4.13.dev3}/portacode/connection/handlers/proxmox_infra.py
RENAMED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
+
import base64
|
|
6
7
|
import json
|
|
7
8
|
import logging
|
|
8
9
|
import os
|
|
@@ -59,7 +60,6 @@ def _emit_progress_event(
|
|
|
59
60
|
message: str,
|
|
60
61
|
phase: str,
|
|
61
62
|
request_id: Optional[str],
|
|
62
|
-
client_sessions: Optional[List[str]] = None,
|
|
63
63
|
details: Optional[Dict[str, Any]] = None,
|
|
64
64
|
) -> None:
|
|
65
65
|
loop = handler.context.get("event_loop")
|
|
@@ -85,8 +85,6 @@ def _emit_progress_event(
|
|
|
85
85
|
payload["request_id"] = request_id
|
|
86
86
|
if details:
|
|
87
87
|
payload["details"] = details
|
|
88
|
-
if client_sessions:
|
|
89
|
-
payload["client_sessions"] = client_sessions
|
|
90
88
|
|
|
91
89
|
future = asyncio.run_coroutine_threadsafe(handler.send_response(payload), loop)
|
|
92
90
|
future.add_done_callback(
|
|
@@ -431,7 +429,12 @@ def _friendly_step_label(step_name: str) -> str:
|
|
|
431
429
|
return normalized.capitalize()
|
|
432
430
|
|
|
433
431
|
|
|
434
|
-
def _build_bootstrap_steps(
|
|
432
|
+
def _build_bootstrap_steps(
|
|
433
|
+
user: str,
|
|
434
|
+
password: str,
|
|
435
|
+
ssh_key: str,
|
|
436
|
+
include_portacode_connect: bool = True,
|
|
437
|
+
) -> List[Dict[str, Any]]:
|
|
435
438
|
steps = [
|
|
436
439
|
{
|
|
437
440
|
"name": "apt_update",
|
|
@@ -468,11 +471,14 @@ def _build_bootstrap_steps(user: str, password: str, ssh_key: str) -> List[Dict[
|
|
|
468
471
|
"cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
|
|
469
472
|
"retries": 0,
|
|
470
473
|
})
|
|
471
|
-
steps.extend(
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
474
|
+
steps.extend(
|
|
475
|
+
[
|
|
476
|
+
{"name": "pip_upgrade", "cmd": "python3 -m pip install --upgrade pip", "retries": 0},
|
|
477
|
+
{"name": "install_portacode", "cmd": "python3 -m pip install --upgrade portacode", "retries": 0},
|
|
478
|
+
]
|
|
479
|
+
)
|
|
480
|
+
if include_portacode_connect:
|
|
481
|
+
steps.append({"name": "portacode_connect", "type": "portacode_connect", "timeout_s": 30})
|
|
476
482
|
return steps
|
|
477
483
|
|
|
478
484
|
|
|
@@ -682,6 +688,36 @@ def _run_pct_check(vmid: int, cmd: str) -> Dict[str, Any]:
|
|
|
682
688
|
return res
|
|
683
689
|
|
|
684
690
|
|
|
691
|
+
def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
|
|
692
|
+
data_dir_cmd = f"su - {user} -c 'echo -n ${{XDG_DATA_HOME:-$HOME/.local/share}}'"
|
|
693
|
+
data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
|
|
694
|
+
return f"{data_home}/portacode/keys"
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
def _write_bytes_as_user(vmid: int, user: str, path: str, data: bytes, mode: int = 0o600) -> None:
|
|
698
|
+
encoded = base64.b64encode(data).decode()
|
|
699
|
+
path_literal = json.dumps(path)
|
|
700
|
+
script = (
|
|
701
|
+
f"su - {user} -c 'python3 - <<\"PY\"\n"
|
|
702
|
+
"import base64\n"
|
|
703
|
+
"from pathlib import Path\n"
|
|
704
|
+
f"path = Path({path_literal})\n"
|
|
705
|
+
"path.parent.mkdir(parents=True, exist_ok=True)\n"
|
|
706
|
+
f"path.write_bytes(base64.b64decode(\"{encoded}\"))\n"
|
|
707
|
+
f"path.chmod({mode})\n"
|
|
708
|
+
"PY'"
|
|
709
|
+
)
|
|
710
|
+
_run_pct_check(vmid, script)
|
|
711
|
+
|
|
712
|
+
|
|
713
|
+
def _deploy_device_keypair(vmid: int, user: str, private_key: str, public_key: str) -> None:
|
|
714
|
+
key_dir = _resolve_portacode_key_dir(vmid, user)
|
|
715
|
+
priv_path = f"{key_dir}/id_portacode"
|
|
716
|
+
pub_path = f"{key_dir}/id_portacode.pub"
|
|
717
|
+
_write_bytes_as_user(vmid, user, priv_path, private_key.encode(), mode=0o600)
|
|
718
|
+
_write_bytes_as_user(vmid, user, pub_path, public_key.encode(), mode=0o644)
|
|
719
|
+
|
|
720
|
+
|
|
685
721
|
def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -> Dict[str, Any]:
|
|
686
722
|
cmd = ["pct", "exec", str(vmid), "--", "bash", "-lc", f"su - {user} -c 'portacode connect'"]
|
|
687
723
|
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
@@ -883,6 +919,7 @@ def _bootstrap_portacode(
|
|
|
883
919
|
progress_callback: Optional[ProgressCallback] = None,
|
|
884
920
|
start_index: int = 1,
|
|
885
921
|
total_steps: Optional[int] = None,
|
|
922
|
+
default_public_key: Optional[str] = None,
|
|
886
923
|
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
887
924
|
actual_steps = steps if steps is not None else _build_bootstrap_steps(user, password, ssh_key)
|
|
888
925
|
results, ok = _run_setup_steps(
|
|
@@ -909,7 +946,7 @@ def _bootstrap_portacode(
|
|
|
909
946
|
raise RuntimeError(f"Portacode bootstrap steps failed: {summary}{history_snippet}")
|
|
910
947
|
raise RuntimeError("Portacode bootstrap steps failed.")
|
|
911
948
|
key_step = next((entry for entry in results if entry.get("name") == "portacode_connect"), None)
|
|
912
|
-
public_key = key_step.get("public_key") if key_step else
|
|
949
|
+
public_key = key_step.get("public_key") if key_step else default_public_key
|
|
913
950
|
if not public_key:
|
|
914
951
|
raise RuntimeError("Portacode connect did not return a public key.")
|
|
915
952
|
return public_key, results
|
|
@@ -1053,16 +1090,19 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1053
1090
|
return "create_proxmox_container"
|
|
1054
1091
|
|
|
1055
1092
|
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
1093
|
+
logger.info("create_proxmox_container command received")
|
|
1056
1094
|
request_id = message.get("request_id")
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
source_client_session,
|
|
1062
|
-
)
|
|
1063
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1095
|
+
device_id = message.get("device_id")
|
|
1096
|
+
device_public_key = (message.get("device_public_key") or "").strip()
|
|
1097
|
+
device_private_key = (message.get("device_private_key") or "").strip()
|
|
1098
|
+
has_device_keypair = bool(device_public_key and device_private_key)
|
|
1064
1099
|
bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
|
|
1065
|
-
bootstrap_steps = _build_bootstrap_steps(
|
|
1100
|
+
bootstrap_steps = _build_bootstrap_steps(
|
|
1101
|
+
bootstrap_user,
|
|
1102
|
+
bootstrap_password,
|
|
1103
|
+
bootstrap_ssh_key,
|
|
1104
|
+
include_portacode_connect=not has_device_keypair,
|
|
1105
|
+
)
|
|
1066
1106
|
total_steps = 3 + len(bootstrap_steps) + 2
|
|
1067
1107
|
current_step_index = 1
|
|
1068
1108
|
|
|
@@ -1084,7 +1124,6 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1084
1124
|
message=start_message,
|
|
1085
1125
|
phase="lifecycle",
|
|
1086
1126
|
request_id=request_id,
|
|
1087
|
-
client_sessions=client_sessions,
|
|
1088
1127
|
)
|
|
1089
1128
|
try:
|
|
1090
1129
|
result = action()
|
|
@@ -1099,7 +1138,6 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1099
1138
|
message=f"{step_label} failed: {exc}",
|
|
1100
1139
|
phase="lifecycle",
|
|
1101
1140
|
request_id=request_id,
|
|
1102
|
-
client_sessions=client_sessions,
|
|
1103
1141
|
details={"error": str(exc)},
|
|
1104
1142
|
)
|
|
1105
1143
|
raise
|
|
@@ -1113,7 +1151,6 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1113
1151
|
message=success_message,
|
|
1114
1152
|
phase="lifecycle",
|
|
1115
1153
|
request_id=request_id,
|
|
1116
|
-
client_sessions=client_sessions,
|
|
1117
1154
|
)
|
|
1118
1155
|
current_step_index += 1
|
|
1119
1156
|
return result
|
|
@@ -1143,7 +1180,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1143
1180
|
payload["cpuunits"] = max(int(payload["cpus"] * 1024), 10)
|
|
1144
1181
|
payload["memory"] = int(payload["ram_mib"])
|
|
1145
1182
|
payload["node"] = node
|
|
1146
|
-
logger.
|
|
1183
|
+
logger.debug(
|
|
1147
1184
|
"Provisioning container node=%s template=%s ram=%s cpu=%s storage=%s",
|
|
1148
1185
|
node,
|
|
1149
1186
|
payload["template"],
|
|
@@ -1156,12 +1193,6 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1156
1193
|
payload["created_at"] = datetime.utcnow().isoformat() + "Z"
|
|
1157
1194
|
payload["status"] = "creating"
|
|
1158
1195
|
_write_container_record(vmid, payload)
|
|
1159
|
-
logger.info(
|
|
1160
|
-
"Container record written vmid=%s hostname=%s client_session=%s",
|
|
1161
|
-
vmid,
|
|
1162
|
-
payload["hostname"],
|
|
1163
|
-
source_client_session,
|
|
1164
|
-
)
|
|
1165
1196
|
return proxmox, node, vmid, payload
|
|
1166
1197
|
|
|
1167
1198
|
proxmox, node, vmid, payload = _run_lifecycle_step(
|
|
@@ -1220,16 +1251,9 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1220
1251
|
message=message_text,
|
|
1221
1252
|
phase="bootstrap",
|
|
1222
1253
|
request_id=request_id,
|
|
1223
|
-
client_sessions=client_sessions,
|
|
1224
1254
|
details=details or None,
|
|
1225
1255
|
)
|
|
1226
1256
|
|
|
1227
|
-
logger.info(
|
|
1228
|
-
"Bootstrapping Portacode in container vmid=%s user=%s client_session=%s",
|
|
1229
|
-
vmid,
|
|
1230
|
-
payload["username"],
|
|
1231
|
-
source_client_session,
|
|
1232
|
-
)
|
|
1233
1257
|
public_key, steps = _bootstrap_portacode(
|
|
1234
1258
|
vmid,
|
|
1235
1259
|
payload["username"],
|
|
@@ -1239,10 +1263,103 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1239
1263
|
progress_callback=_bootstrap_progress_callback,
|
|
1240
1264
|
start_index=current_step_index,
|
|
1241
1265
|
total_steps=total_steps,
|
|
1266
|
+
default_public_key=device_public_key if has_device_keypair else None,
|
|
1242
1267
|
)
|
|
1243
1268
|
current_step_index += len(bootstrap_steps)
|
|
1244
1269
|
|
|
1245
|
-
|
|
1270
|
+
service_installed = False
|
|
1271
|
+
if has_device_keypair:
|
|
1272
|
+
logger.info(
|
|
1273
|
+
"deploying dashboard-provided Portacode keypair (device_id=%s) into container %s",
|
|
1274
|
+
device_id,
|
|
1275
|
+
vmid,
|
|
1276
|
+
)
|
|
1277
|
+
_deploy_device_keypair(
|
|
1278
|
+
vmid,
|
|
1279
|
+
payload["username"],
|
|
1280
|
+
device_private_key,
|
|
1281
|
+
device_public_key,
|
|
1282
|
+
)
|
|
1283
|
+
service_installed = True
|
|
1284
|
+
service_start_index = current_step_index
|
|
1285
|
+
|
|
1286
|
+
auth_step_name = "setup_device_authentication"
|
|
1287
|
+
auth_label = "Setting up device authentication"
|
|
1288
|
+
_emit_progress_event(
|
|
1289
|
+
self,
|
|
1290
|
+
step_index=service_start_index,
|
|
1291
|
+
total_steps=total_steps,
|
|
1292
|
+
step_name=auth_step_name,
|
|
1293
|
+
step_label=auth_label,
|
|
1294
|
+
status="in_progress",
|
|
1295
|
+
message="Notifying the server of the new device…",
|
|
1296
|
+
phase="service",
|
|
1297
|
+
request_id=request_id,
|
|
1298
|
+
)
|
|
1299
|
+
_emit_progress_event(
|
|
1300
|
+
self,
|
|
1301
|
+
step_index=service_start_index,
|
|
1302
|
+
total_steps=total_steps,
|
|
1303
|
+
step_name=auth_step_name,
|
|
1304
|
+
step_label=auth_label,
|
|
1305
|
+
status="completed",
|
|
1306
|
+
message="Authentication metadata recorded.",
|
|
1307
|
+
phase="service",
|
|
1308
|
+
request_id=request_id,
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
install_step = service_start_index + 1
|
|
1312
|
+
install_label = "Launching Portacode service"
|
|
1313
|
+
_emit_progress_event(
|
|
1314
|
+
self,
|
|
1315
|
+
step_index=install_step,
|
|
1316
|
+
total_steps=total_steps,
|
|
1317
|
+
step_name="launch_portacode_service",
|
|
1318
|
+
step_label=install_label,
|
|
1319
|
+
status="in_progress",
|
|
1320
|
+
message="Running sudo portacode service install…",
|
|
1321
|
+
phase="service",
|
|
1322
|
+
request_id=request_id,
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
cmd = f"su - {payload['username']} -c 'sudo -S portacode service install'"
|
|
1326
|
+
res = _run_pct(vmid, cmd, input_text=payload["password"] + "\n")
|
|
1327
|
+
|
|
1328
|
+
if res["returncode"] != 0:
|
|
1329
|
+
_emit_progress_event(
|
|
1330
|
+
self,
|
|
1331
|
+
step_index=install_step,
|
|
1332
|
+
total_steps=total_steps,
|
|
1333
|
+
step_name="launch_portacode_service",
|
|
1334
|
+
step_label=install_label,
|
|
1335
|
+
status="failed",
|
|
1336
|
+
message=f"{install_label} failed: {res.get('stderr') or res.get('stdout')}",
|
|
1337
|
+
phase="service",
|
|
1338
|
+
request_id=request_id,
|
|
1339
|
+
details={
|
|
1340
|
+
"stderr": res.get("stderr"),
|
|
1341
|
+
"stdout": res.get("stdout"),
|
|
1342
|
+
},
|
|
1343
|
+
)
|
|
1344
|
+
raise RuntimeError(res.get("stderr") or res.get("stdout") or "Service install failed")
|
|
1345
|
+
|
|
1346
|
+
_emit_progress_event(
|
|
1347
|
+
self,
|
|
1348
|
+
step_index=install_step,
|
|
1349
|
+
total_steps=total_steps,
|
|
1350
|
+
step_name="launch_portacode_service",
|
|
1351
|
+
step_label=install_label,
|
|
1352
|
+
status="completed",
|
|
1353
|
+
message="Portacode service install finished.",
|
|
1354
|
+
phase="service",
|
|
1355
|
+
request_id=request_id,
|
|
1356
|
+
)
|
|
1357
|
+
|
|
1358
|
+
logger.info("create_proxmox_container: portacode service install completed inside ct %s", vmid)
|
|
1359
|
+
|
|
1360
|
+
current_step_index += 2
|
|
1361
|
+
|
|
1362
|
+
return {
|
|
1246
1363
|
"event": "proxmox_container_created",
|
|
1247
1364
|
"success": True,
|
|
1248
1365
|
"message": f"Container {vmid} is ready and Portacode key captured.",
|
|
@@ -1258,11 +1375,9 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1258
1375
|
"cpus": payload["cpus"],
|
|
1259
1376
|
},
|
|
1260
1377
|
"setup_steps": steps,
|
|
1378
|
+
"device_id": device_id,
|
|
1379
|
+
"service_installed": service_installed,
|
|
1261
1380
|
}
|
|
1262
|
-
if client_sessions:
|
|
1263
|
-
response["client_sessions"] = client_sessions
|
|
1264
|
-
response["_reply_to_source_session"] = True
|
|
1265
|
-
return response
|
|
1266
1381
|
|
|
1267
1382
|
|
|
1268
1383
|
class StartPortacodeServiceHandler(SyncHandler):
|
|
@@ -1290,15 +1405,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1290
1405
|
start_index = int(message.get("step_index", 1))
|
|
1291
1406
|
total_steps = int(message.get("total_steps", start_index + 2))
|
|
1292
1407
|
request_id = message.get("request_id")
|
|
1293
|
-
source_client_session = message.get("source_client_session")
|
|
1294
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1295
|
-
|
|
1296
|
-
logger.info(
|
|
1297
|
-
"start_portacode_service invoked vmid=%s user=%s client_session=%s",
|
|
1298
|
-
vmid,
|
|
1299
|
-
user,
|
|
1300
|
-
source_client_session,
|
|
1301
|
-
)
|
|
1302
1408
|
|
|
1303
1409
|
auth_step_name = "setup_device_authentication"
|
|
1304
1410
|
auth_label = "Setting up device authentication"
|
|
@@ -1312,7 +1418,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1312
1418
|
message="Notifying the server of the new device…",
|
|
1313
1419
|
phase="service",
|
|
1314
1420
|
request_id=request_id,
|
|
1315
|
-
client_sessions=client_sessions,
|
|
1316
1421
|
)
|
|
1317
1422
|
_emit_progress_event(
|
|
1318
1423
|
self,
|
|
@@ -1324,7 +1429,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1324
1429
|
message="Authentication metadata recorded.",
|
|
1325
1430
|
phase="service",
|
|
1326
1431
|
request_id=request_id,
|
|
1327
|
-
client_sessions=client_sessions,
|
|
1328
1432
|
)
|
|
1329
1433
|
|
|
1330
1434
|
install_step = start_index + 1
|
|
@@ -1339,7 +1443,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1339
1443
|
message="Running sudo portacode service install…",
|
|
1340
1444
|
phase="service",
|
|
1341
1445
|
request_id=request_id,
|
|
1342
|
-
client_sessions=client_sessions,
|
|
1343
1446
|
)
|
|
1344
1447
|
|
|
1345
1448
|
cmd = f"su - {user} -c 'sudo -S portacode service install'"
|
|
@@ -1356,7 +1459,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1356
1459
|
message=f"{install_label} failed: {res.get('stderr') or res.get('stdout')}",
|
|
1357
1460
|
phase="service",
|
|
1358
1461
|
request_id=request_id,
|
|
1359
|
-
client_sessions=client_sessions,
|
|
1360
1462
|
details={
|
|
1361
1463
|
"stderr": res.get("stderr"),
|
|
1362
1464
|
"stdout": res.get("stdout"),
|
|
@@ -1364,12 +1466,6 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1364
1466
|
)
|
|
1365
1467
|
raise RuntimeError(res.get("stderr") or res.get("stdout") or "Service install failed")
|
|
1366
1468
|
|
|
1367
|
-
logger.info(
|
|
1368
|
-
"portacode service install command completed vmid=%s returncode=%s",
|
|
1369
|
-
vmid,
|
|
1370
|
-
res["returncode"],
|
|
1371
|
-
)
|
|
1372
|
-
|
|
1373
1469
|
_emit_progress_event(
|
|
1374
1470
|
self,
|
|
1375
1471
|
step_index=install_step,
|
|
@@ -1380,19 +1476,14 @@ class StartPortacodeServiceHandler(SyncHandler):
|
|
|
1380
1476
|
message="Portacode service install finished.",
|
|
1381
1477
|
phase="service",
|
|
1382
1478
|
request_id=request_id,
|
|
1383
|
-
client_sessions=client_sessions,
|
|
1384
1479
|
)
|
|
1385
1480
|
|
|
1386
|
-
|
|
1481
|
+
return {
|
|
1387
1482
|
"event": "proxmox_service_started",
|
|
1388
1483
|
"success": True,
|
|
1389
1484
|
"message": "Portacode service install completed",
|
|
1390
1485
|
"ctid": str(vmid),
|
|
1391
1486
|
}
|
|
1392
|
-
if client_sessions:
|
|
1393
|
-
response["client_sessions"] = client_sessions
|
|
1394
|
-
response["_reply_to_source_session"] = True
|
|
1395
|
-
return response
|
|
1396
1487
|
|
|
1397
1488
|
|
|
1398
1489
|
class StartProxmoxContainerHandler(SyncHandler):
|
|
@@ -1408,20 +1499,12 @@ class StartProxmoxContainerHandler(SyncHandler):
|
|
|
1408
1499
|
proxmox = _connect_proxmox(config)
|
|
1409
1500
|
node = _get_node_from_config(config)
|
|
1410
1501
|
_ensure_container_managed(proxmox, node, vmid)
|
|
1411
|
-
source_client_session = message.get("source_client_session")
|
|
1412
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1413
|
-
|
|
1414
|
-
logger.info(
|
|
1415
|
-
"start_proxmox_container invoked vmid=%s client_session=%s",
|
|
1416
|
-
vmid,
|
|
1417
|
-
source_client_session,
|
|
1418
|
-
)
|
|
1419
1502
|
|
|
1420
1503
|
status, elapsed = _start_container(proxmox, node, vmid)
|
|
1421
1504
|
_update_container_record(vmid, {"status": "running"})
|
|
1422
1505
|
|
|
1423
1506
|
infra = get_infra_snapshot()
|
|
1424
|
-
|
|
1507
|
+
return {
|
|
1425
1508
|
"event": "proxmox_container_action",
|
|
1426
1509
|
"action": "start",
|
|
1427
1510
|
"success": True,
|
|
@@ -1431,10 +1514,6 @@ class StartProxmoxContainerHandler(SyncHandler):
|
|
|
1431
1514
|
"status": status.get("status"),
|
|
1432
1515
|
"infra": infra,
|
|
1433
1516
|
}
|
|
1434
|
-
if client_sessions:
|
|
1435
|
-
response["client_sessions"] = client_sessions
|
|
1436
|
-
response["_reply_to_source_session"] = True
|
|
1437
|
-
return response
|
|
1438
1517
|
|
|
1439
1518
|
|
|
1440
1519
|
class StopProxmoxContainerHandler(SyncHandler):
|
|
@@ -1450,14 +1529,6 @@ class StopProxmoxContainerHandler(SyncHandler):
|
|
|
1450
1529
|
proxmox = _connect_proxmox(config)
|
|
1451
1530
|
node = _get_node_from_config(config)
|
|
1452
1531
|
_ensure_container_managed(proxmox, node, vmid)
|
|
1453
|
-
source_client_session = message.get("source_client_session")
|
|
1454
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1455
|
-
|
|
1456
|
-
logger.info(
|
|
1457
|
-
"stop_proxmox_container invoked vmid=%s client_session=%s",
|
|
1458
|
-
vmid,
|
|
1459
|
-
source_client_session,
|
|
1460
|
-
)
|
|
1461
1532
|
|
|
1462
1533
|
status, elapsed = _stop_container(proxmox, node, vmid)
|
|
1463
1534
|
final_status = status.get("status") or "stopped"
|
|
@@ -1469,7 +1540,7 @@ class StopProxmoxContainerHandler(SyncHandler):
|
|
|
1469
1540
|
if final_status != "running" and elapsed == 0.0
|
|
1470
1541
|
else f"Stopped container {vmid} in {elapsed:.1f}s."
|
|
1471
1542
|
)
|
|
1472
|
-
|
|
1543
|
+
return {
|
|
1473
1544
|
"event": "proxmox_container_action",
|
|
1474
1545
|
"action": "stop",
|
|
1475
1546
|
"success": True,
|
|
@@ -1479,10 +1550,6 @@ class StopProxmoxContainerHandler(SyncHandler):
|
|
|
1479
1550
|
"status": final_status,
|
|
1480
1551
|
"infra": infra,
|
|
1481
1552
|
}
|
|
1482
|
-
if client_sessions:
|
|
1483
|
-
response["client_sessions"] = client_sessions
|
|
1484
|
-
response["_reply_to_source_session"] = True
|
|
1485
|
-
return response
|
|
1486
1553
|
|
|
1487
1554
|
|
|
1488
1555
|
class RemoveProxmoxContainerHandler(SyncHandler):
|
|
@@ -1498,21 +1565,13 @@ class RemoveProxmoxContainerHandler(SyncHandler):
|
|
|
1498
1565
|
proxmox = _connect_proxmox(config)
|
|
1499
1566
|
node = _get_node_from_config(config)
|
|
1500
1567
|
_ensure_container_managed(proxmox, node, vmid)
|
|
1501
|
-
source_client_session = message.get("source_client_session")
|
|
1502
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1503
|
-
|
|
1504
|
-
logger.info(
|
|
1505
|
-
"remove_proxmox_container invoked vmid=%s client_session=%s",
|
|
1506
|
-
vmid,
|
|
1507
|
-
source_client_session,
|
|
1508
|
-
)
|
|
1509
1568
|
|
|
1510
1569
|
stop_status, stop_elapsed = _stop_container(proxmox, node, vmid)
|
|
1511
1570
|
delete_status, delete_elapsed = _delete_container(proxmox, node, vmid)
|
|
1512
1571
|
_remove_container_record(vmid)
|
|
1513
1572
|
|
|
1514
1573
|
infra = get_infra_snapshot()
|
|
1515
|
-
|
|
1574
|
+
return {
|
|
1516
1575
|
"event": "proxmox_container_action",
|
|
1517
1576
|
"action": "remove",
|
|
1518
1577
|
"success": True,
|
|
@@ -1525,10 +1584,6 @@ class RemoveProxmoxContainerHandler(SyncHandler):
|
|
|
1525
1584
|
"status": "deleted",
|
|
1526
1585
|
"infra": infra,
|
|
1527
1586
|
}
|
|
1528
|
-
if client_sessions:
|
|
1529
|
-
response["client_sessions"] = client_sessions
|
|
1530
|
-
response["_reply_to_source_session"] = True
|
|
1531
|
-
return response
|
|
1532
1587
|
|
|
1533
1588
|
|
|
1534
1589
|
class ConfigureProxmoxInfraHandler(SyncHandler):
|
|
@@ -1542,19 +1597,13 @@ class ConfigureProxmoxInfraHandler(SyncHandler):
|
|
|
1542
1597
|
verify_ssl = bool(message.get("verify_ssl"))
|
|
1543
1598
|
if not token_identifier or not token_value:
|
|
1544
1599
|
raise ValueError("token_identifier and token_value are required")
|
|
1545
|
-
source_client_session = message.get("source_client_session")
|
|
1546
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1547
1600
|
snapshot = configure_infrastructure(token_identifier, token_value, verify_ssl=verify_ssl)
|
|
1548
|
-
|
|
1601
|
+
return {
|
|
1549
1602
|
"event": "proxmox_infra_configured",
|
|
1550
1603
|
"success": True,
|
|
1551
1604
|
"message": "Proxmox infrastructure configured",
|
|
1552
1605
|
"infra": snapshot,
|
|
1553
1606
|
}
|
|
1554
|
-
if client_sessions:
|
|
1555
|
-
response["client_sessions"] = client_sessions
|
|
1556
|
-
response["_reply_to_source_session"] = True
|
|
1557
|
-
return response
|
|
1558
1607
|
|
|
1559
1608
|
|
|
1560
1609
|
class RevertProxmoxInfraHandler(SyncHandler):
|
|
@@ -1563,16 +1612,10 @@ class RevertProxmoxInfraHandler(SyncHandler):
|
|
|
1563
1612
|
return "revert_proxmox_infra"
|
|
1564
1613
|
|
|
1565
1614
|
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
1566
|
-
source_client_session = message.get("source_client_session")
|
|
1567
|
-
client_sessions = [source_client_session] if source_client_session else None
|
|
1568
1615
|
snapshot = revert_infrastructure()
|
|
1569
|
-
|
|
1616
|
+
return {
|
|
1570
1617
|
"event": "proxmox_infra_reverted",
|
|
1571
1618
|
"success": True,
|
|
1572
1619
|
"message": "Proxmox infrastructure configuration reverted",
|
|
1573
1620
|
"infra": snapshot,
|
|
1574
1621
|
}
|
|
1575
|
-
if client_sessions:
|
|
1576
|
-
response["client_sessions"] = client_sessions
|
|
1577
|
-
response["_reply_to_source_session"] = True
|
|
1578
|
-
return response
|