portacode 1.4.12.dev9__tar.gz → 1.4.15.dev0__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.12.dev9 → portacode-1.4.15.dev0}/PKG-INFO +1 -1
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/_version.py +2 -2
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +12 -3
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/proxmox_infra.py +284 -37
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/system_handlers.py +131 -2
- portacode-1.4.15.dev0/portacode/connection/handlers/test_proxmox_infra.py +13 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/SOURCES.txt +1 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/.claude/agents/communication-manager.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/.claude/settings.local.json +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/.gitignore +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/.gitmodules +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/LICENSE +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/MANIFEST.in +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/Makefile +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/backup.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/connect.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/connect.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docker-compose.yaml +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docs/images/device-transfer-button.png +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docs/images/device-transfer-modal.png +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docs/images/pair-device-button.png +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docs/images/pairing-request.png +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/docs/images/student-workspace.png +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/simple_device/Dockerfile +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/simple_device/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/simple_device/docker-compose.yaml +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/Dockerfile +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/docker-compose.yaml +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/manage.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/__main__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/cli.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/client.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/base.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/chunked_content.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/diff_handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/git_manager.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/manager.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state/utils.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/registry.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/session.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/update_handler.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/multiplex.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/terminal.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/data.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/keypair.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/elinks +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/gio-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/gnome-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/gvfs-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/kde-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/kfmclient +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/links +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/links2 +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/lynx +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/mate-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/netsurf +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/sensible-browser +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/w3m +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/x-www-browser +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/link_capture/bin/xdg-open +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/logging_categories.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/pairing.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/service.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/static/js/test-ntp-clock.html +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/static/js/utils/ntp-clock.js +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/utils/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/utils/diff_apply.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/utils/diff_renderer.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/utils/ntp_clock.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/requires.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode.egg-info/top_level.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/pyproject.toml +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/restore.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/run_tests.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/setup.cfg +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/setup.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_device_online.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_file_operations.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_git_status_ui.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_login_flow.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_play_store_screenshots.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_terminal_buffer_performance.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_terminal_loading_race_condition.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_modules/test_terminal_start.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/test_request_id.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/.env.example +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/README.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/cli.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/__init__.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/base_test.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/cli_manager.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/runner.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/core/test_discovery.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/testing_framework/requirements.txt +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/agent_context_management.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/django_server_time_sync.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/device_performance_degradation.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/indefinite_resource_loading.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/portacode_service_silently_down.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/premature_terminal_exit.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/project_cpu_hotspots.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/terminals_exit_upon_starting.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/todo/smartphone_terminal_input_frustrations.md +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/tools/generate_play_store_assets.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/tools/pairing_tester.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/tools/run_screenshot_suite.sh +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/tools/test_python_ntp_clock.py +0 -0
- {portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4,
|
|
31
|
+
__version__ = version = '1.4.15.dev0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev0')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
{portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md
RENAMED
|
@@ -326,9 +326,10 @@ Configures a Proxmox node for Portacode infrastructure usage (API token validati
|
|
|
326
326
|
|
|
327
327
|
**Payload Fields:**
|
|
328
328
|
|
|
329
|
-
* `token_identifier` (string,
|
|
330
|
-
* `token_value` (string,
|
|
331
|
-
* `verify_ssl` (boolean, optional): When true, the handler verifies SSL certificates; defaults to `false`.
|
|
329
|
+
* `token_identifier` (string, optional when reconfiguring): API token identifier in the form `user@realm!tokenid`. Required on first configuration or when replacing the stored token.
|
|
330
|
+
* `token_value` (string, optional when reconfiguring): Secret value associated with the token. Required when `token_identifier` is supplied.
|
|
331
|
+
* `verify_ssl` (boolean, optional): When true, the handler verifies SSL certificates; defaults to `false`. When omitted, the last configured value is preserved.
|
|
332
|
+
* `cloudflare_api_token` (string, optional): Cloudflare API token the host can reuse later to provision tunnels.
|
|
332
333
|
|
|
333
334
|
**Responses:**
|
|
334
335
|
|
|
@@ -361,6 +362,9 @@ Creates a Portacode-managed LXC container, starts it, and bootstraps the Portaco
|
|
|
361
362
|
* `username` (string, optional): OS user to provision (defaults to `svcuser`).
|
|
362
363
|
* `password` (string, optional): Password for the user (used only during provisioning).
|
|
363
364
|
* `ssh_key` (string, optional): SSH public key to add to the user.
|
|
365
|
+
* `device_id` (string, optional): ID of the Device record that already exists on the dashboard.
|
|
366
|
+
* `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.
|
|
367
|
+
* `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.
|
|
364
368
|
|
|
365
369
|
**Responses:**
|
|
366
370
|
|
|
@@ -418,6 +422,8 @@ Emitted after a successful `create_proxmox_container` action. Contains the new c
|
|
|
418
422
|
* `public_key` (string): Portacode public auth key created inside the new container.
|
|
419
423
|
* `container` (object): Metadata such as `vmid`, `hostname`, `template`, `storage`, `disk_gib`, `ram_mib`, and `cpus`.
|
|
420
424
|
* `setup_steps` (array[object]): Detailed bootstrap step results (name, stdout/stderr, elapsed time, and status).
|
|
425
|
+
* `device_id` (string, optional): Mirrors the `device_id` supplied with `create_proxmox_container`, if any.
|
|
426
|
+
* `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`.
|
|
421
427
|
|
|
422
428
|
### `proxmox_container_progress`
|
|
423
429
|
|
|
@@ -450,6 +456,7 @@ Runs `sudo portacode service install` inside the container after the dashboard h
|
|
|
450
456
|
* Emits additional [`proxmox_container_progress`](#proxmox_container_progress-event) events to report the authentication and service-install steps.
|
|
451
457
|
* On success, emits a [`proxmox_service_started`](#proxmox_service_started-event).
|
|
452
458
|
* On failure, emits a generic [`error`](#error) event.
|
|
459
|
+
* 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.
|
|
453
460
|
|
|
454
461
|
### `proxmox_service_started`
|
|
455
462
|
|
|
@@ -1120,6 +1127,8 @@ Provides system information in response to a `system_info` action. Handled by [`
|
|
|
1120
1127
|
* `message` (string|null): Informational text about the network setup attempt.
|
|
1121
1128
|
* `bridge` (string): The bridge interface configured (typically `vmbr1`).
|
|
1122
1129
|
* `health` (string|null): `"healthy"` when the connectivity verification succeeded.
|
|
1130
|
+
* `cloudflare` (object): Optional Cloudflare metadata collected for future tunnels:
|
|
1131
|
+
* `configured` (boolean): True when a Cloudflare API token is stored.
|
|
1123
1132
|
* `node_status` (object|null): Status response returned by the Proxmox API when validating the token.
|
|
1124
1133
|
* `managed_containers` (object): Cached summary of the Portacode-managed containers:
|
|
1125
1134
|
* `updated_at` (string): ISO timestamp when this snapshot was last refreshed.
|
{portacode-1.4.12.dev9 → portacode-1.4.15.dev0}/portacode/connection/handlers/proxmox_infra.py
RENAMED
|
@@ -7,15 +7,17 @@ import json
|
|
|
7
7
|
import logging
|
|
8
8
|
import os
|
|
9
9
|
import secrets
|
|
10
|
+
import shlex
|
|
10
11
|
import shutil
|
|
11
12
|
import stat
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
15
|
+
import tempfile
|
|
14
16
|
import time
|
|
15
17
|
import threading
|
|
16
18
|
from datetime import datetime
|
|
17
19
|
from pathlib import Path
|
|
18
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
|
20
|
+
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
|
|
19
21
|
|
|
20
22
|
import platformdirs
|
|
21
23
|
|
|
@@ -261,7 +263,10 @@ def _ensure_bridge(bridge: str = DEFAULT_BRIDGE) -> Dict[str, Any]:
|
|
|
261
263
|
apt = shutil.which("apt-get")
|
|
262
264
|
if not apt:
|
|
263
265
|
raise RuntimeError("dnsmasq is missing and apt-get unavailable to install it")
|
|
264
|
-
_call_subprocess([apt, "update"], check=
|
|
266
|
+
update = _call_subprocess([apt, "update"], check=False)
|
|
267
|
+
if update.returncode not in (0, 100):
|
|
268
|
+
msg = update.stderr or update.stdout or f"exit status {update.returncode}"
|
|
269
|
+
raise RuntimeError(f"apt-get update failed: {msg}")
|
|
265
270
|
_call_subprocess([apt, "install", "-y", "dnsmasq"], check=True)
|
|
266
271
|
_write_bridge_config(bridge)
|
|
267
272
|
_ensure_sysctl()
|
|
@@ -428,7 +433,12 @@ def _friendly_step_label(step_name: str) -> str:
|
|
|
428
433
|
return normalized.capitalize()
|
|
429
434
|
|
|
430
435
|
|
|
431
|
-
def _build_bootstrap_steps(
|
|
436
|
+
def _build_bootstrap_steps(
|
|
437
|
+
user: str,
|
|
438
|
+
password: str,
|
|
439
|
+
ssh_key: str,
|
|
440
|
+
include_portacode_connect: bool = True,
|
|
441
|
+
) -> List[Dict[str, Any]]:
|
|
432
442
|
steps = [
|
|
433
443
|
{
|
|
434
444
|
"name": "apt_update",
|
|
@@ -465,11 +475,14 @@ def _build_bootstrap_steps(user: str, password: str, ssh_key: str) -> List[Dict[
|
|
|
465
475
|
"cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
|
|
466
476
|
"retries": 0,
|
|
467
477
|
})
|
|
468
|
-
steps.extend(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
478
|
+
steps.extend(
|
|
479
|
+
[
|
|
480
|
+
{"name": "pip_upgrade", "cmd": "python3 -m pip install --upgrade pip", "retries": 0},
|
|
481
|
+
{"name": "install_portacode", "cmd": "python3 -m pip install --upgrade portacode", "retries": 0},
|
|
482
|
+
]
|
|
483
|
+
)
|
|
484
|
+
if include_portacode_connect:
|
|
485
|
+
steps.append({"name": "portacode_connect", "type": "portacode_connect", "timeout_s": 30})
|
|
473
486
|
return steps
|
|
474
487
|
|
|
475
488
|
|
|
@@ -679,6 +692,74 @@ def _run_pct_check(vmid: int, cmd: str) -> Dict[str, Any]:
|
|
|
679
692
|
return res
|
|
680
693
|
|
|
681
694
|
|
|
695
|
+
def _run_pct_exec(vmid: int, command: Sequence[str]) -> subprocess.CompletedProcess[str]:
|
|
696
|
+
return _call_subprocess(["pct", "exec", str(vmid), "--", *command])
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
def _run_pct_exec_check(vmid: int, command: Sequence[str]) -> subprocess.CompletedProcess[str]:
|
|
700
|
+
res = _run_pct_exec(vmid, command)
|
|
701
|
+
if res.returncode != 0:
|
|
702
|
+
raise RuntimeError(res.stderr or res.stdout or f"pct exec {' '.join(command)} failed")
|
|
703
|
+
return res
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
def _run_pct_push(vmid: int, src: str, dest: str) -> subprocess.CompletedProcess[str]:
|
|
707
|
+
return _call_subprocess(["pct", "push", str(vmid), src, dest])
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _push_bytes_to_container(
|
|
711
|
+
vmid: int, user: str, path: str, data: bytes, mode: int = 0o600
|
|
712
|
+
) -> None:
|
|
713
|
+
logger.debug("Preparing to push %d bytes to container vmid=%s path=%s for user=%s", len(data), vmid, path, user)
|
|
714
|
+
tmp_path: Optional[str] = None
|
|
715
|
+
try:
|
|
716
|
+
parent = Path(path).parent
|
|
717
|
+
parent_str = parent.as_posix()
|
|
718
|
+
if parent_str not in {"", ".", "/"}:
|
|
719
|
+
_run_pct_exec_check(vmid, ["mkdir", "-p", parent_str])
|
|
720
|
+
_run_pct_exec_check(vmid, ["chown", "-R", f"{user}:{user}", parent_str])
|
|
721
|
+
|
|
722
|
+
with tempfile.NamedTemporaryFile(delete=False) as tmp:
|
|
723
|
+
tmp.write(data)
|
|
724
|
+
tmp.flush()
|
|
725
|
+
os.fsync(tmp.fileno())
|
|
726
|
+
tmp_path = tmp.name
|
|
727
|
+
|
|
728
|
+
push_res = _run_pct_push(vmid, tmp_path, path)
|
|
729
|
+
if push_res.returncode != 0:
|
|
730
|
+
raise RuntimeError(push_res.stderr or push_res.stdout or f"pct push returned {push_res.returncode}")
|
|
731
|
+
|
|
732
|
+
_run_pct_exec_check(vmid, ["chown", f"{user}:{user}", path])
|
|
733
|
+
_run_pct_exec_check(vmid, ["chmod", format(mode, "o"), path])
|
|
734
|
+
logger.debug("Successfully pushed %d bytes to vmid=%s path=%s", len(data), vmid, path)
|
|
735
|
+
except Exception as exc:
|
|
736
|
+
logger.error("Failed to write to container vmid=%s path=%s for user=%s: %s", vmid, path, user, exc)
|
|
737
|
+
raise
|
|
738
|
+
finally:
|
|
739
|
+
if tmp_path:
|
|
740
|
+
try:
|
|
741
|
+
os.remove(tmp_path)
|
|
742
|
+
except OSError as cleanup_exc:
|
|
743
|
+
logger.warning("Failed to remove temporary file %s: %s", tmp_path, cleanup_exc)
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
|
|
747
|
+
data_dir_cmd = f"su - {user} -c 'echo -n ${{XDG_DATA_HOME:-$HOME/.local/share}}'"
|
|
748
|
+
data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
|
|
749
|
+
portacode_dir = f"{data_home}/portacode"
|
|
750
|
+
_run_pct_exec_check(vmid, ["mkdir", "-p", portacode_dir])
|
|
751
|
+
_run_pct_exec_check(vmid, ["chown", "-R", f"{user}:{user}", portacode_dir])
|
|
752
|
+
return f"{portacode_dir}/keys"
|
|
753
|
+
|
|
754
|
+
|
|
755
|
+
def _deploy_device_keypair(vmid: int, user: str, private_key: str, public_key: str) -> None:
|
|
756
|
+
key_dir = _resolve_portacode_key_dir(vmid, user)
|
|
757
|
+
priv_path = f"{key_dir}/id_portacode"
|
|
758
|
+
pub_path = f"{key_dir}/id_portacode.pub"
|
|
759
|
+
_push_bytes_to_container(vmid, user, priv_path, private_key.encode(), mode=0o600)
|
|
760
|
+
_push_bytes_to_container(vmid, user, pub_path, public_key.encode(), mode=0o644)
|
|
761
|
+
|
|
762
|
+
|
|
682
763
|
def _portacode_connect_and_read_key(vmid: int, user: str, timeout_s: int = 10) -> Dict[str, Any]:
|
|
683
764
|
cmd = ["pct", "exec", str(vmid), "--", "bash", "-lc", f"su - {user} -c 'portacode connect'"]
|
|
684
765
|
proc = subprocess.Popen(cmd, text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
@@ -880,6 +961,7 @@ def _bootstrap_portacode(
|
|
|
880
961
|
progress_callback: Optional[ProgressCallback] = None,
|
|
881
962
|
start_index: int = 1,
|
|
882
963
|
total_steps: Optional[int] = None,
|
|
964
|
+
default_public_key: Optional[str] = None,
|
|
883
965
|
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
884
966
|
actual_steps = steps if steps is not None else _build_bootstrap_steps(user, password, ssh_key)
|
|
885
967
|
results, ok = _run_setup_steps(
|
|
@@ -897,21 +979,38 @@ def _bootstrap_portacode(
|
|
|
897
979
|
history_snippet = ""
|
|
898
980
|
if isinstance(history, list) and history:
|
|
899
981
|
history_snippet = f" history={history[-3:]}"
|
|
982
|
+
command = details.get("cmd")
|
|
983
|
+
command_text = ""
|
|
984
|
+
if command:
|
|
985
|
+
if isinstance(command, (list, tuple)):
|
|
986
|
+
command_text = shlex.join(str(entry) for entry in command)
|
|
987
|
+
else:
|
|
988
|
+
command_text = str(command)
|
|
989
|
+
command_suffix = f" command={command_text}" if command_text else ""
|
|
900
990
|
if summary:
|
|
901
991
|
logger.warning(
|
|
902
|
-
"Portacode bootstrap failure summary=%s%s",
|
|
992
|
+
"Portacode bootstrap failure summary=%s%s%s",
|
|
903
993
|
summary,
|
|
904
994
|
f" history_len={len(history)}" if history else "",
|
|
995
|
+
f" command={command_text}" if command_text else "",
|
|
996
|
+
)
|
|
997
|
+
raise RuntimeError(
|
|
998
|
+
f"Portacode bootstrap steps failed: {summary}{history_snippet}{command_suffix}"
|
|
905
999
|
)
|
|
906
|
-
raise RuntimeError(f"Portacode bootstrap steps failed: {summary}{history_snippet}")
|
|
907
1000
|
raise RuntimeError("Portacode bootstrap steps failed.")
|
|
908
1001
|
key_step = next((entry for entry in results if entry.get("name") == "portacode_connect"), None)
|
|
909
|
-
public_key = key_step.get("public_key") if key_step else
|
|
1002
|
+
public_key = key_step.get("public_key") if key_step else default_public_key
|
|
910
1003
|
if not public_key:
|
|
911
1004
|
raise RuntimeError("Portacode connect did not return a public key.")
|
|
912
1005
|
return public_key, results
|
|
913
1006
|
|
|
914
1007
|
|
|
1008
|
+
def _build_cloudflare_snapshot(cloudflare_config: Dict[str, Any] | None) -> Dict[str, Any]:
|
|
1009
|
+
if not cloudflare_config:
|
|
1010
|
+
return {"configured": False}
|
|
1011
|
+
return {"configured": bool(cloudflare_config.get("api_token"))}
|
|
1012
|
+
|
|
1013
|
+
|
|
915
1014
|
def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
916
1015
|
network = config.get("network", {})
|
|
917
1016
|
base_network = {
|
|
@@ -919,8 +1018,13 @@ def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
919
1018
|
"message": network.get("message"),
|
|
920
1019
|
"bridge": network.get("bridge", DEFAULT_BRIDGE),
|
|
921
1020
|
}
|
|
1021
|
+
cloudflare_snapshot = _build_cloudflare_snapshot(config.get("cloudflare"))
|
|
922
1022
|
if not config:
|
|
923
|
-
return {
|
|
1023
|
+
return {
|
|
1024
|
+
"configured": False,
|
|
1025
|
+
"network": base_network,
|
|
1026
|
+
"cloudflare": cloudflare_snapshot,
|
|
1027
|
+
}
|
|
924
1028
|
return {
|
|
925
1029
|
"configured": True,
|
|
926
1030
|
"host": config.get("host"),
|
|
@@ -931,18 +1035,54 @@ def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
931
1035
|
"templates": config.get("templates") or [],
|
|
932
1036
|
"last_verified": config.get("last_verified"),
|
|
933
1037
|
"network": base_network,
|
|
1038
|
+
"cloudflare": cloudflare_snapshot,
|
|
934
1039
|
}
|
|
935
1040
|
|
|
936
1041
|
|
|
937
|
-
def
|
|
1042
|
+
def _resolve_proxmox_credentials(
|
|
1043
|
+
token_identifier: Optional[str],
|
|
1044
|
+
token_value: Optional[str],
|
|
1045
|
+
existing: Dict[str, Any],
|
|
1046
|
+
) -> Tuple[str, str, str]:
|
|
1047
|
+
if token_identifier:
|
|
1048
|
+
if not token_value:
|
|
1049
|
+
raise ValueError("token_value is required when providing a new token_identifier")
|
|
1050
|
+
user, token_name = _parse_token(token_identifier)
|
|
1051
|
+
return user, token_name, token_value
|
|
1052
|
+
if existing and existing.get("user") and existing.get("token_name") and existing.get("token_value"):
|
|
1053
|
+
return existing["user"], existing["token_name"], existing["token_value"]
|
|
1054
|
+
raise ValueError("Proxmox token identifier and value are required when no existing configuration is available")
|
|
1055
|
+
|
|
1056
|
+
|
|
1057
|
+
def _build_cloudflare_config(existing: Dict[str, Any], api_token: Optional[str]) -> Dict[str, Any]:
|
|
1058
|
+
cloudflare: Dict[str, Any] = dict(existing.get("cloudflare", {}) or {})
|
|
1059
|
+
if api_token:
|
|
1060
|
+
cloudflare["api_token"] = api_token
|
|
1061
|
+
if cloudflare.get("api_token"):
|
|
1062
|
+
cloudflare["configured"] = True
|
|
1063
|
+
elif "configured" in cloudflare:
|
|
1064
|
+
cloudflare.pop("configured", None)
|
|
1065
|
+
return cloudflare
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
def configure_infrastructure(
|
|
1069
|
+
token_identifier: Optional[str] = None,
|
|
1070
|
+
token_value: Optional[str] = None,
|
|
1071
|
+
verify_ssl: Optional[bool] = None,
|
|
1072
|
+
cloudflare_api_token: Optional[str] = None,
|
|
1073
|
+
) -> Dict[str, Any]:
|
|
938
1074
|
ProxmoxAPI = _ensure_proxmoxer()
|
|
939
|
-
|
|
1075
|
+
existing = _load_config()
|
|
1076
|
+
user, token_name, resolved_token_value = _resolve_proxmox_credentials(
|
|
1077
|
+
token_identifier, token_value, existing
|
|
1078
|
+
)
|
|
1079
|
+
actual_verify_ssl = verify_ssl if verify_ssl is not None else existing.get("verify_ssl", False)
|
|
940
1080
|
client = ProxmoxAPI(
|
|
941
1081
|
DEFAULT_HOST,
|
|
942
1082
|
user=user,
|
|
943
1083
|
token_name=token_name,
|
|
944
|
-
token_value=
|
|
945
|
-
verify_ssl=
|
|
1084
|
+
token_value=resolved_token_value,
|
|
1085
|
+
verify_ssl=actual_verify_ssl,
|
|
946
1086
|
timeout=30,
|
|
947
1087
|
)
|
|
948
1088
|
node = _pick_node(client)
|
|
@@ -950,35 +1090,35 @@ def configure_infrastructure(token_identifier: str, token_value: str, verify_ssl
|
|
|
950
1090
|
storages = client.nodes(node).storage.get()
|
|
951
1091
|
default_storage = _pick_storage(storages)
|
|
952
1092
|
templates = _list_templates(client, node, storages)
|
|
953
|
-
network
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
1093
|
+
network = dict(existing.get("network", {}) or {})
|
|
1094
|
+
if not network.get("applied"):
|
|
1095
|
+
try:
|
|
1096
|
+
network = _ensure_bridge()
|
|
1097
|
+
# Wait for network convergence before validating connectivity
|
|
1098
|
+
time.sleep(2)
|
|
1099
|
+
if not _verify_connectivity():
|
|
1100
|
+
raise RuntimeError("Connectivity check failed; bridge reverted")
|
|
959
1101
|
network["health"] = "healthy"
|
|
960
|
-
|
|
961
|
-
|
|
1102
|
+
except Exception as exc:
|
|
1103
|
+
logger.warning("Bridge setup failed; reverting previous changes: %s", exc)
|
|
962
1104
|
_revert_bridge()
|
|
963
|
-
|
|
964
|
-
network = {"applied": False, "message": str(exc), "bridge": DEFAULT_BRIDGE}
|
|
965
|
-
logger.warning("Bridge setup skipped: %s", exc)
|
|
966
|
-
except Exception as exc: # pragma: no cover - best effort
|
|
967
|
-
network = {"applied": False, "message": str(exc), "bridge": DEFAULT_BRIDGE}
|
|
968
|
-
logger.warning("Bridge setup failed: %s", exc)
|
|
1105
|
+
raise
|
|
969
1106
|
config = {
|
|
970
1107
|
"host": DEFAULT_HOST,
|
|
971
1108
|
"node": node,
|
|
972
1109
|
"user": user,
|
|
973
1110
|
"token_name": token_name,
|
|
974
|
-
"token_value":
|
|
975
|
-
"verify_ssl":
|
|
1111
|
+
"token_value": resolved_token_value,
|
|
1112
|
+
"verify_ssl": actual_verify_ssl,
|
|
976
1113
|
"default_storage": default_storage,
|
|
977
1114
|
"templates": templates,
|
|
978
1115
|
"last_verified": datetime.utcnow().isoformat() + "Z",
|
|
979
1116
|
"network": network,
|
|
980
1117
|
"node_status": status,
|
|
981
1118
|
}
|
|
1119
|
+
cloudflare = _build_cloudflare_config(existing, cloudflare_api_token)
|
|
1120
|
+
if cloudflare:
|
|
1121
|
+
config["cloudflare"] = cloudflare
|
|
982
1122
|
_save_config(config)
|
|
983
1123
|
snapshot = build_snapshot(config)
|
|
984
1124
|
snapshot["node_status"] = status
|
|
@@ -1052,8 +1192,17 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1052
1192
|
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
1053
1193
|
logger.info("create_proxmox_container command received")
|
|
1054
1194
|
request_id = message.get("request_id")
|
|
1195
|
+
device_id = message.get("device_id")
|
|
1196
|
+
device_public_key = (message.get("device_public_key") or "").strip()
|
|
1197
|
+
device_private_key = (message.get("device_private_key") or "").strip()
|
|
1198
|
+
has_device_keypair = bool(device_public_key and device_private_key)
|
|
1055
1199
|
bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
|
|
1056
|
-
bootstrap_steps = _build_bootstrap_steps(
|
|
1200
|
+
bootstrap_steps = _build_bootstrap_steps(
|
|
1201
|
+
bootstrap_user,
|
|
1202
|
+
bootstrap_password,
|
|
1203
|
+
bootstrap_ssh_key,
|
|
1204
|
+
include_portacode_connect=not has_device_keypair,
|
|
1205
|
+
)
|
|
1057
1206
|
total_steps = 3 + len(bootstrap_steps) + 2
|
|
1058
1207
|
current_step_index = 1
|
|
1059
1208
|
|
|
@@ -1214,9 +1363,102 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1214
1363
|
progress_callback=_bootstrap_progress_callback,
|
|
1215
1364
|
start_index=current_step_index,
|
|
1216
1365
|
total_steps=total_steps,
|
|
1366
|
+
default_public_key=device_public_key if has_device_keypair else None,
|
|
1217
1367
|
)
|
|
1218
1368
|
current_step_index += len(bootstrap_steps)
|
|
1219
1369
|
|
|
1370
|
+
service_installed = False
|
|
1371
|
+
if has_device_keypair:
|
|
1372
|
+
logger.info(
|
|
1373
|
+
"deploying dashboard-provided Portacode keypair (device_id=%s) into container %s",
|
|
1374
|
+
device_id,
|
|
1375
|
+
vmid,
|
|
1376
|
+
)
|
|
1377
|
+
_deploy_device_keypair(
|
|
1378
|
+
vmid,
|
|
1379
|
+
payload["username"],
|
|
1380
|
+
device_private_key,
|
|
1381
|
+
device_public_key,
|
|
1382
|
+
)
|
|
1383
|
+
service_installed = True
|
|
1384
|
+
service_start_index = current_step_index
|
|
1385
|
+
|
|
1386
|
+
auth_step_name = "setup_device_authentication"
|
|
1387
|
+
auth_label = "Setting up device authentication"
|
|
1388
|
+
_emit_progress_event(
|
|
1389
|
+
self,
|
|
1390
|
+
step_index=service_start_index,
|
|
1391
|
+
total_steps=total_steps,
|
|
1392
|
+
step_name=auth_step_name,
|
|
1393
|
+
step_label=auth_label,
|
|
1394
|
+
status="in_progress",
|
|
1395
|
+
message="Notifying the server of the new device…",
|
|
1396
|
+
phase="service",
|
|
1397
|
+
request_id=request_id,
|
|
1398
|
+
)
|
|
1399
|
+
_emit_progress_event(
|
|
1400
|
+
self,
|
|
1401
|
+
step_index=service_start_index,
|
|
1402
|
+
total_steps=total_steps,
|
|
1403
|
+
step_name=auth_step_name,
|
|
1404
|
+
step_label=auth_label,
|
|
1405
|
+
status="completed",
|
|
1406
|
+
message="Authentication metadata recorded.",
|
|
1407
|
+
phase="service",
|
|
1408
|
+
request_id=request_id,
|
|
1409
|
+
)
|
|
1410
|
+
|
|
1411
|
+
install_step = service_start_index + 1
|
|
1412
|
+
install_label = "Launching Portacode service"
|
|
1413
|
+
_emit_progress_event(
|
|
1414
|
+
self,
|
|
1415
|
+
step_index=install_step,
|
|
1416
|
+
total_steps=total_steps,
|
|
1417
|
+
step_name="launch_portacode_service",
|
|
1418
|
+
step_label=install_label,
|
|
1419
|
+
status="in_progress",
|
|
1420
|
+
message="Running sudo portacode service install…",
|
|
1421
|
+
phase="service",
|
|
1422
|
+
request_id=request_id,
|
|
1423
|
+
)
|
|
1424
|
+
|
|
1425
|
+
cmd = f"su - {payload['username']} -c 'sudo -S portacode service install'"
|
|
1426
|
+
res = _run_pct(vmid, cmd, input_text=payload["password"] + "\n")
|
|
1427
|
+
|
|
1428
|
+
if res["returncode"] != 0:
|
|
1429
|
+
_emit_progress_event(
|
|
1430
|
+
self,
|
|
1431
|
+
step_index=install_step,
|
|
1432
|
+
total_steps=total_steps,
|
|
1433
|
+
step_name="launch_portacode_service",
|
|
1434
|
+
step_label=install_label,
|
|
1435
|
+
status="failed",
|
|
1436
|
+
message=f"{install_label} failed: {res.get('stderr') or res.get('stdout')}",
|
|
1437
|
+
phase="service",
|
|
1438
|
+
request_id=request_id,
|
|
1439
|
+
details={
|
|
1440
|
+
"stderr": res.get("stderr"),
|
|
1441
|
+
"stdout": res.get("stdout"),
|
|
1442
|
+
},
|
|
1443
|
+
)
|
|
1444
|
+
raise RuntimeError(res.get("stderr") or res.get("stdout") or "Service install failed")
|
|
1445
|
+
|
|
1446
|
+
_emit_progress_event(
|
|
1447
|
+
self,
|
|
1448
|
+
step_index=install_step,
|
|
1449
|
+
total_steps=total_steps,
|
|
1450
|
+
step_name="launch_portacode_service",
|
|
1451
|
+
step_label=install_label,
|
|
1452
|
+
status="completed",
|
|
1453
|
+
message="Portacode service install finished.",
|
|
1454
|
+
phase="service",
|
|
1455
|
+
request_id=request_id,
|
|
1456
|
+
)
|
|
1457
|
+
|
|
1458
|
+
logger.info("create_proxmox_container: portacode service install completed inside ct %s", vmid)
|
|
1459
|
+
|
|
1460
|
+
current_step_index += 2
|
|
1461
|
+
|
|
1220
1462
|
return {
|
|
1221
1463
|
"event": "proxmox_container_created",
|
|
1222
1464
|
"success": True,
|
|
@@ -1233,6 +1475,8 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1233
1475
|
"cpus": payload["cpus"],
|
|
1234
1476
|
},
|
|
1235
1477
|
"setup_steps": steps,
|
|
1478
|
+
"device_id": device_id,
|
|
1479
|
+
"service_installed": service_installed,
|
|
1236
1480
|
}
|
|
1237
1481
|
|
|
1238
1482
|
|
|
@@ -1450,10 +1694,13 @@ class ConfigureProxmoxInfraHandler(SyncHandler):
|
|
|
1450
1694
|
def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
|
|
1451
1695
|
token_identifier = message.get("token_identifier")
|
|
1452
1696
|
token_value = message.get("token_value")
|
|
1453
|
-
verify_ssl =
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1697
|
+
verify_ssl = message.get("verify_ssl")
|
|
1698
|
+
snapshot = configure_infrastructure(
|
|
1699
|
+
token_identifier=token_identifier,
|
|
1700
|
+
token_value=token_value,
|
|
1701
|
+
verify_ssl=verify_ssl,
|
|
1702
|
+
cloudflare_api_token=message.get("cloudflare_api_token"),
|
|
1703
|
+
)
|
|
1457
1704
|
return {
|
|
1458
1705
|
"event": "proxmox_infra_configured",
|
|
1459
1706
|
"success": True,
|