portacode 2.2.22.dev0__tar.gz → 2.2.22.dev1__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-2.2.22.dev0 → portacode-2.2.22.dev1}/PKG-INFO +1 -1
- portacode-2.2.22.dev1/docs/template-engineering-roadmap.md +414 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/_version.py +2 -2
- portacode-2.2.22.dev1/portacode/browser_prepare.py +117 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/codex_prepare.py +21 -6
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/proxmox_infra.py +57 -105
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_browser_handlers.py +4 -4
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_proxmox_infra.py +12 -27
- portacode-2.2.22.dev1/portacode/tests/test_browser_prepare.py +123 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_codex_prepare.py +38 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode.egg-info/PKG-INFO +1 -1
- portacode-2.2.22.dev1/portacode.egg-info/SOURCES.txt +232 -0
- portacode-2.2.22.dev0/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -2
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -137
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -69
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -28
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/README.md +0 -38
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -87
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -13
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/manage.py +0 -22
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/requirements.txt +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -85
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/admin.py +0 -3
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/apps.py +0 -6
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/menu.py +0 -43
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -1
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/models.py +0 -11
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/tests.py +0 -15
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/urls.py +0 -9
- portacode-2.2.22.dev0/examples/workshop_fleet/initial_content/treats/views.py +0 -22
- portacode-2.2.22.dev0/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/portacode/link_capture/__pycache__/__init__.cpython-38.pyc +0 -0
- portacode-2.2.22.dev0/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
- portacode-2.2.22.dev0/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-38.pyc +0 -0
- portacode-2.2.22.dev0/portacode.egg-info/SOURCES.txt +0 -450
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/.gitignore +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/LICENSE +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/MANIFEST.in +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/Makefile +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/build_android.sh +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/connect.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/connect.sh +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/cloudflared-domain-connect-containers-audit.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/creative-team-brief-portacode.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/device-health-alerting-plan.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/devops-messaging-ab-tests.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/guest-host-requests-plan.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/homepage-dashboard-positioning-fixes.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/docs/template-guide.html +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/simple_device/Dockerfile +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/simple_device/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/simple_device/docker-compose.yaml +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/workshop_fleet/Dockerfile +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/workshop_fleet/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/workshop_fleet/docker-compose.yaml +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/README.md +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/galactic_bakeshop/__init__.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/galactic_bakeshop/asgi.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/galactic_bakeshop/settings.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/galactic_bakeshop/urls.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/galactic_bakeshop/wsgi.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/manage.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/requirements.txt +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/templates/treats/home.html +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/__init__.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/admin.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/apps.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/menu.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/migrations/__init__.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/models.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/tests.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-01/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/urls.py +0 -0
- {portacode-2.2.22.dev0/examples/workshop_fleet/data/student-02/workspace → portacode-2.2.22.dev1/examples/workshop_fleet/initial_content}/treats/views.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/__main__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/assets/skills/portacode-github/SKILL.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/assets/skills/portacode-github/agents/openai.yaml +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/assets/skills/portacode-github/scripts/create_repository.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/assets/skills/portacode-image/SKILL.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/assets/skills/portacode-image/agents/openai.yaml +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/browser_cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/browser_runtime.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/codex_loopback_proxy.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/codex_usage_limit.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/client.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/codex_app_server.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/guest_host_protocol.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/automation_v2_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/base.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/browser_handlers.py +3 -3
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/chunked_content.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/cloudflare_forwarding.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/cloudflare_tunnel.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/codex_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/diff_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/file_transfer_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/git_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/git_manager.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/manager.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/test_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state/utils.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/registry.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/resumable_transfer_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/runtime_user.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/session.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_cloudflare_forwarding.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_file_move_copy.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_git_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/test_runtime_user.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/handlers/update_handler.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/multiplex.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/terminal.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/test_guest_host_protocol.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/test_guest_host_terminal.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/test_webmin_proxy_config.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/connection/webmin_proxy_config.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/data.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/exit_codes.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/github_credential.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/image_cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/keypair.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/elinks +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/gio-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/gnome-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/gvfs-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/kde-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/kfmclient +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/links +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/links2 +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/lynx +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/mate-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/netsurf +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/sensible-browser +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/w3m +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/x-www-browser +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/link_capture/bin/xdg-open +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/logging_categories.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/pairing.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/provisioning_cache.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/restart.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/service.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/singleton.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/static/js/test-ntp-clock.html +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/static/js/utils/ntp-clock.js +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/test_runtime_paths.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/test_service.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/test_singleton.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/test_terminal_exposed_services.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_automation_v2_inputs.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_browser_cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_cli_service_logs.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_codex_app_server.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_codex_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_codex_loopback_proxy.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_github_credential.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_image_cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_image_read_handler.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_resumable_transfer_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_system_handlers.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_terminal_periodic_system_info.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tests/test_terminal_session_shell.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/cloudflared_login.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/ensure_cloudflared.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/ensure_pyyaml.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/forwarding_state.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/get_domain.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/privileged.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/service_install.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/tunneling/state.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/updater.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/diff_apply.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/diff_renderer.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/ntp_clock.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode/utils/runtime_paths.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode.egg-info/requires.txt +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/portacode.egg-info/top_level.txt +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/pyproject.toml +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/run_tests.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/scripts/diagnose_codex_gateway.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/setup.cfg +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/setup.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test.sh +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_device_online.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_file_operations.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_git_status_ui.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_login_flow.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_play_store_screenshots.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_terminal_buffer_performance.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_terminal_loading_race_condition.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_modules/test_terminal_start.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_request_id.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/test_session_terminal_shutdown.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/.env.example +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/README.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/cli.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/__init__.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/base_test.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/cli_manager.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/runner.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/core/test_discovery.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/testing_framework/requirements.txt +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/UI_UX/server_occasionally_stops_communicating_with_all_devices.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/agent_context_management.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/django_server_time_sync.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/device_performance_degradation.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/indefinite_resource_loading.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/portacode_service_silently_down.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/premature_terminal_exit.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/project_cpu_hotspots.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/terminals_exit_upon_starting.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/websocket_client_silently_dead.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/todo/smartphone_terminal_input_frustrations.md +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/tools/generate_play_store_assets.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/tools/pairing_tester.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/tools/run_screenshot_suite.sh +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/tools/test_python_ntp_clock.py +0 -0
- {portacode-2.2.22.dev0 → portacode-2.2.22.dev1}/validate.sh +0 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Template engineering architecture roadmap
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
A user should be able to tell Dashboard AI, “create a reusable template for this
|
|
6
|
+
project.” Portacode should then derive a Portafile, provision clean containers,
|
|
7
|
+
execute and observe it, repair failures, repeat trials from controlled snapshots,
|
|
8
|
+
verify the real user journey, and save a versioned template with evidence of how
|
|
9
|
+
reliable it is. The same object should support private reuse, controlled sharing,
|
|
10
|
+
catalog publication, and optional screenshot/video capture for marketing.
|
|
11
|
+
|
|
12
|
+
This roadmap is ordered by the best ratio of user-visible progress to implementation
|
|
13
|
+
risk. Each step is independently useful and leaves a foundation for the next one.
|
|
14
|
+
|
|
15
|
+
## What exists today
|
|
16
|
+
|
|
17
|
+
The product already has most of the execution primitives, but they are not assembled
|
|
18
|
+
into a template-engineering system:
|
|
19
|
+
|
|
20
|
+
- Published templates are Django CMS pages. `OneClickTemplateExtension` stores an
|
|
21
|
+
opaque `yaml_config` text field plus a description. Model validation proves only
|
|
22
|
+
that the value is non-empty, parseable YAML.
|
|
23
|
+
- Source-controlled catalog files under `content/one-click/` can be synchronized with
|
|
24
|
+
CMS and exported back to Markdown. This is a staff content workflow, not a user
|
|
25
|
+
template repository.
|
|
26
|
+
- Dashboard AI can search the published catalog and read a template's YAML, but it
|
|
27
|
+
has no create, update, clone, validate, test, or publish template operation.
|
|
28
|
+
- Dashboard AI can provision a container from a published template or arbitrary
|
|
29
|
+
customized YAML, start an automation on an existing device, inspect its state,
|
|
30
|
+
execute bounded terminal commands, delegate constrained Codex work, and operate a
|
|
31
|
+
browser.
|
|
32
|
+
- Managed Proxmox containers expose list/create/delete/rollback snapshot operations
|
|
33
|
+
to Dashboard AI. These operations are isolated host requests; there is no durable
|
|
34
|
+
experiment controller tying snapshots to a candidate template or trial.
|
|
35
|
+
- `AutomationTask` durably records instructions, metadata, the original YAML, current
|
|
36
|
+
step, status, and a bounded failure. It represents one execution, not a trial in a
|
|
37
|
+
template version's validation campaign.
|
|
38
|
+
- Portafile parsing accepts aliases and silently drops unrecognized/malformed steps
|
|
39
|
+
in places. Inputs receive stronger validation, but there is no single strict,
|
|
40
|
+
versioned Portafile schema or normalized intermediate representation shared by
|
|
41
|
+
CMS, provisioning, automation, and AI tools.
|
|
42
|
+
- Automation defaults to AI-assisted recovery for Dashboard AI runs, which is useful
|
|
43
|
+
operationally but can hide whether the submitted template itself is reproducible.
|
|
44
|
+
- `dashboard_browser_run` can assert a real browser journey and capture screenshots
|
|
45
|
+
or a low-resolution video. Media is returned for the current tool interaction or
|
|
46
|
+
offered as a device download; it is not attached to a template version with
|
|
47
|
+
provenance, consent, retention, or publication state.
|
|
48
|
+
|
|
49
|
+
In short, Portacode has an executor and a capable AI operator. It is missing the
|
|
50
|
+
domain model and scientific harness that turn trial-and-error into a trustworthy,
|
|
51
|
+
reusable artifact.
|
|
52
|
+
|
|
53
|
+
## Architectural direction: a moderated template-baking session
|
|
54
|
+
|
|
55
|
+
The primary abstraction should optimize for the Dashboard AI, not for YAML authors.
|
|
56
|
+
From the AI's perspective, baking a template should feel like incrementally building
|
|
57
|
+
a project:
|
|
58
|
+
|
|
59
|
+
1. start a baking session;
|
|
60
|
+
2. propose the next logical step and its success checks;
|
|
61
|
+
3. receive either actionable admission feedback or the executed result;
|
|
62
|
+
4. on success, continue from the new checkpoint;
|
|
63
|
+
5. on failure, receive detailed evidence with an explicit guarantee that the device
|
|
64
|
+
has already been rolled back and restarted at the previous successful checkpoint;
|
|
65
|
+
6. finish the session and let Portacode compile the archived steps into a Portafile.
|
|
66
|
+
|
|
67
|
+
Snapshots, process state, retries, rollback, restart, logs, and archival should be
|
|
68
|
+
backend invariants. The AI must not have to call those tools in the right order or
|
|
69
|
+
retain that state in its context.
|
|
70
|
+
|
|
71
|
+
Here, deterministic means **contract-reliable, not output-identical**. A useful
|
|
72
|
+
template is a parameterized project generator. Different valid input sets may produce
|
|
73
|
+
different logos, copy, features, integrations, layouts, records, and AI-generated
|
|
74
|
+
customizations. Reliability means that each result honors its inputs, satisfies the
|
|
75
|
+
same declared structural and behavioral invariants, and remains operable. Tests should
|
|
76
|
+
therefore compare observable contracts and input-specific expectations, not complete
|
|
77
|
+
filesystem hashes or pixel-identical pages.
|
|
78
|
+
|
|
79
|
+
Keep Portafile as the portable output, but introduce a durable baking domain around
|
|
80
|
+
it:
|
|
81
|
+
|
|
82
|
+
```text
|
|
83
|
+
TemplateBake
|
|
84
|
+
-> BakeRevision
|
|
85
|
+
-> BakeStep (proposed operation + semantic verdict + archived definition)
|
|
86
|
+
-> StepAttempt (snapshot, execution, observations, outcome)
|
|
87
|
+
-> AssertionResult / Artifact
|
|
88
|
+
-> Compiled TemplateVersion
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Use a deterministic state machine as the moderator. Dashboard AI decides what useful
|
|
92
|
+
project change to try next; the moderator owns admission, tool policy, snapshot
|
|
93
|
+
discipline, execution, rollback/start, evidence collection, retry count, and final
|
|
94
|
+
compilation. This is substantially safer and easier than exposing all primitives and
|
|
95
|
+
prompting the AI to orchestrate them correctly.
|
|
96
|
+
|
|
97
|
+
## Prioritized implementation steps
|
|
98
|
+
|
|
99
|
+
### 1. Add durable baking models and one composite `dashboard_bake_template_step` tool
|
|
100
|
+
|
|
101
|
+
This is the new smallest high-leverage vertical slice. Add `TemplateBake`,
|
|
102
|
+
`BakeRevision`, `BakeStep`, and `StepAttempt`. A bake is bound to one user, Dashboard
|
|
103
|
+
chat, managed disposable device, clean baseline snapshot, and a versioned input
|
|
104
|
+
contract. That contract supports typed free text, long text, number, boolean, select,
|
|
105
|
+
multiselect, secret, file/image, color, URL, and integration-reference inputs, with
|
|
106
|
+
labels, help text, constraints, defaults, and safe persistence policy. A step contains:
|
|
107
|
+
|
|
108
|
+
- a stable ID and human goal;
|
|
109
|
+
- a typed operation (`command`, `wait_for`, constrained `codex`, browser check, file
|
|
110
|
+
operation, or supported infrastructure declaration);
|
|
111
|
+
- deterministic success assertions;
|
|
112
|
+
- its semantic admission verdict and risk scores;
|
|
113
|
+
- the pre-step snapshot name;
|
|
114
|
+
- every attempt and bounded stdout/stderr/automation/browser evidence;
|
|
115
|
+
- final state (`proposed`, `rejected`, `running`, `archived`, `failed_rolled_back`).
|
|
116
|
+
|
|
117
|
+
Every value used by a step must be classified as a constant, declared template input,
|
|
118
|
+
Portacode runtime value, prior-step output, or secret reference. Derived values should
|
|
119
|
+
be recorded through typed output bindings rather than scraped informally from logs.
|
|
120
|
+
For example, a CRM bake can bind `brand_logo`, `industry`, `sales_pipeline`, and
|
|
121
|
+
`company_description`, while Portacode supplies the exposed URL and generated service
|
|
122
|
+
credentials at runtime.
|
|
123
|
+
|
|
124
|
+
Expose one composite tool. Internally it must:
|
|
125
|
+
|
|
126
|
+
1. validate structure and permissions;
|
|
127
|
+
2. run semantic admission;
|
|
128
|
+
3. create a snapshot of the last successful state;
|
|
129
|
+
4. execute through existing device/automation/browser services;
|
|
130
|
+
5. evaluate assertions independently;
|
|
131
|
+
6. archive the successful normalized step; or
|
|
132
|
+
7. on failure, roll back, explicitly start the stopped container, wait for the device
|
|
133
|
+
to reconnect, verify the previous checkpoint, and only then return to the AI.
|
|
134
|
+
|
|
135
|
+
The failure result should lead with `ROLLED_BACK_AND_READY`, identify the last
|
|
136
|
+
successful step, and include command, exit code, timeout state, bounded stdout/stderr,
|
|
137
|
+
failed assertions, semantic warnings, and artifact references. If rollback/start or
|
|
138
|
+
reconnection fails, return `RECOVERY_INCOMPLETE` and block further steps; never tell
|
|
139
|
+
the AI it is safe to continue.
|
|
140
|
+
|
|
141
|
+
**Why first:** it immediately removes orchestration burden from the LLM and proves the
|
|
142
|
+
moderated workflow while reusing current provisioning, automation, snapshot, terminal,
|
|
143
|
+
Codex, and browser implementations.
|
|
144
|
+
|
|
145
|
+
**Exit criteria:** the AI can build a two-step project using only the baking tool; a
|
|
146
|
+
forced failure returns detailed evidence after verified rollback and restart, and a
|
|
147
|
+
subsequent corrected step proceeds from the last archived checkpoint.
|
|
148
|
+
|
|
149
|
+
### 2. Add a dedicated semantic template-step admission engine
|
|
150
|
+
|
|
151
|
+
Factor the metered Responses API pattern used by `_semantic_validate_work_order` into
|
|
152
|
+
a reusable admission client, but give template steps a separate prompt, response
|
|
153
|
+
schema, usage nonce, audit record, and policy. Run deterministic checks first, then
|
|
154
|
+
send the normalized proposed step plus relevant bake context—not secrets or unbounded
|
|
155
|
+
logs—to `gpt-5.6-terra` in the same Dashboard thread budget.
|
|
156
|
+
|
|
157
|
+
The response must be strict JSON with:
|
|
158
|
+
|
|
159
|
+
- `decision`: `accept`, `accept_with_risk`, or `reject`;
|
|
160
|
+
- `template_suitability`: 0–100;
|
|
161
|
+
- `nondeterminism`: 0–100;
|
|
162
|
+
- `portability_risk`, `secret_risk`, and `destructive_risk`: 0–100;
|
|
163
|
+
- typed findings with exact field, evidence, explanation, and repair guidance;
|
|
164
|
+
- detected runtime-derived values that must become placeholders/inputs;
|
|
165
|
+
- input-usage findings, including undeclared values, ignored declared inputs, unsafe
|
|
166
|
+
interpolation, missing validation, and behavior that contradicts an input choice;
|
|
167
|
+
- required assertions and a recommended clean-success trial count from 1–5.
|
|
168
|
+
|
|
169
|
+
The prompt should detect at least hard-coded hostnames such as
|
|
170
|
+
`*.host.exposify.net`, device IDs, IPs, usernames/home paths, ports that should use
|
|
171
|
+
`[exposed:PORT]`, credentials/tokens, local absolute paths, ephemeral URLs, generated
|
|
172
|
+
resource names, interactive commands, unpinned moving dependencies, time/randomness,
|
|
173
|
+
external mutable services, and Codex/LLM execution. Hard-coding is not uniformly
|
|
174
|
+
wrong: `localhost`, conventional service paths, and an application's intentional
|
|
175
|
+
fixed port may be valid. The engine must explain contextual findings rather than
|
|
176
|
+
reject every literal.
|
|
177
|
+
|
|
178
|
+
Reject security violations, operations outside bake policy, steps that cannot be
|
|
179
|
+
represented portably, and steps without independently observable completion. Accept
|
|
180
|
+
nondeterministic steps when bounded and verifiable, then use their score to raise the
|
|
181
|
+
trial count. Semantic admission is defense in depth, not the sole security boundary;
|
|
182
|
+
allowlists, argument validation, and authorization remain deterministic.
|
|
183
|
+
|
|
184
|
+
Do not penalize legitimate parameterization or bounded generation merely because
|
|
185
|
+
outputs differ. Penalize unexplained variability, undeclared environmental dependence,
|
|
186
|
+
and lack of assertions. Semantic review should ask “does this operation reliably
|
|
187
|
+
transform every allowed input into an output satisfying the contract?” rather than
|
|
188
|
+
“will this operation produce identical bytes?”
|
|
189
|
+
|
|
190
|
+
**Exit criteria:** equivalent steps receive a typed, stored verdict; rejected steps
|
|
191
|
+
never execute or create a success archive; usage is charged and auditable in the same
|
|
192
|
+
thread; malformed model output fails closed.
|
|
193
|
+
|
|
194
|
+
### 3. Enforce a baking-specific capability profile instead of rebuilding device tools
|
|
195
|
+
|
|
196
|
+
Add a bake-session capability filter at the Dashboard tool-selection and gateway
|
|
197
|
+
authorization layers. While a bake is active, hide/block direct mutation tools that
|
|
198
|
+
could bypass admission or checkpointing. The AI should normally retain only:
|
|
199
|
+
|
|
200
|
+
- the composite step tool;
|
|
201
|
+
- bake state/history and finish/abort tools;
|
|
202
|
+
- read-only device/file/image/automation inspection;
|
|
203
|
+
- template/docs search;
|
|
204
|
+
- user input and permission requests.
|
|
205
|
+
|
|
206
|
+
Direct terminal execution, file writes/deletes, Codex tasks, power control, resize,
|
|
207
|
+
snapshot mutation, automation start/retry, ingress/domain changes, transfers, and
|
|
208
|
+
repository publication should be denied during baking unless invoked internally by
|
|
209
|
+
the moderator under a capability scoped to the current step. Browser reads/assertions
|
|
210
|
+
are safe through the moderator; browser actions that mutate application state should
|
|
211
|
+
be declared in the step so reset behavior is known.
|
|
212
|
+
|
|
213
|
+
Use two layers because hiding tools is UX, not security. The service layer must reject
|
|
214
|
+
a direct call carrying a bake-bound device/session even if a stale model or client
|
|
215
|
+
still knows the tool name. Do not globally cripple the Dashboard AI: these restrictions
|
|
216
|
+
apply only to the disposable bake target while the bake is active.
|
|
217
|
+
|
|
218
|
+
**Exit criteria:** attempts to bypass moderation through every mutating device tool
|
|
219
|
+
fail server-side, while ordinary non-baking device work is unchanged.
|
|
220
|
+
|
|
221
|
+
### 4. Make the checkpoint state machine crash-safe and snapshot-aware
|
|
222
|
+
|
|
223
|
+
Implement step execution as a durable worker/state machine with idempotency keys and
|
|
224
|
+
explicit phases: `admitting`, `snapshotting`, `executing`, `asserting`, `archiving`,
|
|
225
|
+
`rolling_back`, `starting`, `reconnecting`, and terminal states. Snapshot names should
|
|
226
|
+
derive from bake/step IDs, not model-generated strings.
|
|
227
|
+
|
|
228
|
+
Account explicitly for Proxmox rollback stopping the container. Recovery is complete
|
|
229
|
+
only after rollback succeeds, start succeeds, the device reconnects, and a checkpoint
|
|
230
|
+
probe passes. Resume correctly after worker, web, host, or device reconnects without
|
|
231
|
+
duplicating execution. Route snapshot/power requests through the existing
|
|
232
|
+
WebSocket-owning process/durable guest-host boundary; background workers must not call
|
|
233
|
+
process-local singleton waiters directly.
|
|
234
|
+
|
|
235
|
+
Successful steps become immutable archived definitions. Keep the current success
|
|
236
|
+
snapshot as the next baseline and garbage-collect superseded snapshots only after the
|
|
237
|
+
new checkpoint is proven. Failed attempt evidence remains linked even after rollback.
|
|
238
|
+
|
|
239
|
+
**Exit criteria:** faults injected at every phase converge to one correct state,
|
|
240
|
+
successful commands are not accidentally rerun, and no failure destroys its evidence
|
|
241
|
+
or strands a stopped container unnoticed.
|
|
242
|
+
|
|
243
|
+
### 5. Turn input contracts and nondeterminism scores into automatic isolated trials
|
|
244
|
+
|
|
245
|
+
Separate exploratory baking from proof. Generate a compact validation matrix from the
|
|
246
|
+
declared input contract. It should include a representative/default persona, meaningful
|
|
247
|
+
select/boolean branches, boundary-valid text and numbers, alternate logos/files, empty
|
|
248
|
+
optional values, and safe Unicode/spacing cases. Authors may add named personas such
|
|
249
|
+
as `small_retail_crm` and `consulting_team_crm` with input-specific expectations.
|
|
250
|
+
Avoid the full Cartesian product; use pairwise/branch coverage plus explicitly critical
|
|
251
|
+
combinations.
|
|
252
|
+
|
|
253
|
+
After a step first succeeds, low-risk steps can be archived immediately but still
|
|
254
|
+
participate in final clean-room replay. Risky steps trigger automatic independent
|
|
255
|
+
attempts from their pre-step snapshot. Roll back before every attempt, start/reconnect,
|
|
256
|
+
apply a selected input fixture and fresh secrets, execute, assert, and reset again.
|
|
257
|
+
|
|
258
|
+
Cap required successful attempts at five. A sensible initial mapping is 1 success for
|
|
259
|
+
scores 0–19, 2 for 20–39, 3 for 40–59, 4 for 60–79, and 5 for 80–100. Also cap total
|
|
260
|
+
attempts (for example seven) so failures cannot loop indefinitely. Mark results as
|
|
261
|
+
`5/5 clean attempts`, not “almost always reliable”; five samples do not justify a
|
|
262
|
+
production reliability percentage. Any unexplained failure prevents a strong
|
|
263
|
+
validated status and is returned to the AI for repair.
|
|
264
|
+
|
|
265
|
+
Codex steps must be constrained work orders and must have deterministic external
|
|
266
|
+
postconditions. Their generated copy, layout, or implementation may differ across
|
|
267
|
+
attempts, but assertions must prove the selected logo/copy/options were honored and
|
|
268
|
+
the resulting application has the required flows, persistence, security, and health.
|
|
269
|
+
Use semantic or visual quality review only for criteria that genuinely cannot be
|
|
270
|
+
encoded deterministically, and store its rubric and verdict as evidence. Never count
|
|
271
|
+
the same post-success filesystem state multiple times, and never let AI auto-repair
|
|
272
|
+
inside an attempt count as a pass.
|
|
273
|
+
|
|
274
|
+
**Exit criteria:** the campaign covers declared input branches and representative
|
|
275
|
+
personas; nondeterministic success produces multiple isolated attempt records without
|
|
276
|
+
the Dashboard AI orchestrating retries; and the stored status reports both trial count
|
|
277
|
+
and input/branch coverage rather than implying that one fixture proves the template.
|
|
278
|
+
|
|
279
|
+
### 6. Compile archived steps and run a final clean-room replay
|
|
280
|
+
|
|
281
|
+
When the AI finishes, compile only admitted, archived steps into a normalized,
|
|
282
|
+
versioned Portafile. Convert discovered runtime values into declared inputs or
|
|
283
|
+
Portacode placeholders. Compile the typed input contract, conditional behavior,
|
|
284
|
+
bindings, validation rules, and input-specific acceptance checks alongside stable step
|
|
285
|
+
IDs. Preserve the distinction between user-provided files, secrets, runtime-generated
|
|
286
|
+
values, and reusable non-secret defaults.
|
|
287
|
+
|
|
288
|
+
Provision a fresh container—do not use the incrementally baked one—and execute the
|
|
289
|
+
whole compiled template from zero with interactive repair disabled. Apply the
|
|
290
|
+
nondeterminism policy to the complete template as well as individual risky steps.
|
|
291
|
+
Only this clean-room replay can mark a version validated.
|
|
292
|
+
|
|
293
|
+
**Exit criteria:** the emitted Portafile reproduces the project on a new container and
|
|
294
|
+
has a digest linked to all admission decisions, step attempts, and final trials.
|
|
295
|
+
|
|
296
|
+
### 7. Persist private user templates and immutable versions
|
|
297
|
+
|
|
298
|
+
Add `UserTemplate` and immutable `TemplateVersion` ownership, revision, visibility,
|
|
299
|
+
fork lineage, normalized manifest, digest, lifecycle, and reliability summary. The
|
|
300
|
+
bake compiles into a private draft by default. Secrets remain references to declared
|
|
301
|
+
inputs and are never copied from attempts into YAML, logs, or artifacts.
|
|
302
|
+
|
|
303
|
+
**Exit criteria:** Dashboard AI can finish a bake, save the resulting private version,
|
|
304
|
+
and deploy that exact digest again; cross-user access fails closed.
|
|
305
|
+
|
|
306
|
+
### 8. Make reuse and controlled sharing a product workflow
|
|
307
|
+
|
|
308
|
+
Add clone/fork, export/import, archive, and visibility (`private`, `unlisted`,
|
|
309
|
+
`public`) operations. Deployments should always reference a version digest so later
|
|
310
|
+
author edits cannot change an old deployment. Sharing should exclude saved input
|
|
311
|
+
values, credentials, trial secrets, private logs, private URLs, and unpublished
|
|
312
|
+
artifacts.
|
|
313
|
+
|
|
314
|
+
Introduce explicit license/attribution metadata and moderation/security scanning for
|
|
315
|
+
public versions. A shared version is immutable; recipients fork to edit it. Add
|
|
316
|
+
revocation/deprecation without breaking the audit history of prior deployments.
|
|
317
|
+
|
|
318
|
+
**Exit criteria:** two users can safely reuse the same public/unlisted version, one
|
|
319
|
+
cannot modify the other's artifact, and export then import preserves its digest and
|
|
320
|
+
schema version.
|
|
321
|
+
|
|
322
|
+
### 9. Promote validated versions into the public catalog
|
|
323
|
+
|
|
324
|
+
Replace the split authority gradually: render public one-click pages from approved
|
|
325
|
+
template versions or create a publication record that projects a version into CMS.
|
|
326
|
+
Keep editorial copy/localization in CMS, while deployment YAML, evidence, versioning,
|
|
327
|
+
and trust state come from the template domain.
|
|
328
|
+
|
|
329
|
+
Require publication gates: schema pass, campaign policy pass, security/license scan,
|
|
330
|
+
resource bounds, human approval where policy requires it, and a rollback/deprecation
|
|
331
|
+
plan. Show users version, last validated date, tested environments, pass/attempt
|
|
332
|
+
counts, required privileges, inputs, and known limitations.
|
|
333
|
+
|
|
334
|
+
**Exit criteria:** no public page can serve YAML different from its approved version
|
|
335
|
+
digest, and catalog updates do not require copying opaque YAML between CMS and source
|
|
336
|
+
files by hand.
|
|
337
|
+
|
|
338
|
+
### 10. Add declarative Playwright journeys and durable marketing assets
|
|
339
|
+
|
|
340
|
+
Build on the existing bounded browser-operation vocabulary rather than embedding
|
|
341
|
+
arbitrary Playwright programs in YAML. Add authoring-only `journeys` composed of
|
|
342
|
+
semantic operations and assertions, plus named capture points and optional bounded
|
|
343
|
+
video recording. Separate `acceptance` journeys from `marketing` capture requests;
|
|
344
|
+
marketing capture must never determine deployment success.
|
|
345
|
+
|
|
346
|
+
Persist artifacts in managed object storage with template version, trial, step,
|
|
347
|
+
viewport, locale, timestamp, checksum, MIME type, provenance, consent/publication
|
|
348
|
+
state, and retention policy. Sanitize sessions and scan screenshots/video for secrets,
|
|
349
|
+
personal data, private hostnames, tokens, and debug overlays before publication.
|
|
350
|
+
Generate marketing assets only from a clean successful trial using deterministic seed
|
|
351
|
+
data and an explicitly publishable input profile.
|
|
352
|
+
|
|
353
|
+
**Exit criteria:** a version can reproducibly generate approved screenshots/video
|
|
354
|
+
from its declared journey, while failed/redacted/private captures remain inaccessible
|
|
355
|
+
to public catalog pages.
|
|
356
|
+
|
|
357
|
+
### 11. Operate the system as a reliability service
|
|
358
|
+
|
|
359
|
+
Add campaign queues, per-user/node concurrency and cost limits, disposable-container
|
|
360
|
+
leases, orphan reconciliation, artifact retention jobs, dependency caching policy,
|
|
361
|
+
and dashboards for pass rate, flakiness, duration, AI spend, infra spend, and cleanup
|
|
362
|
+
failures. Periodically revalidate public templates against newer source images and
|
|
363
|
+
dependency versions without silently promoting results to a different template
|
|
364
|
+
version.
|
|
365
|
+
|
|
366
|
+
Use failure clustering to find common platform/template issues, but keep automated
|
|
367
|
+
recovery inside the owning service and preserve root-cause evidence. Publish status
|
|
368
|
+
only from durable records.
|
|
369
|
+
|
|
370
|
+
**Exit criteria:** interrupted campaigns converge to a correct terminal state,
|
|
371
|
+
resources are reclaimed, costs are bounded, and maintainers can distinguish platform
|
|
372
|
+
regressions from template regressions.
|
|
373
|
+
|
|
374
|
+
## Recommended first delivery slice
|
|
375
|
+
|
|
376
|
+
Implement the narrow vertical path through steps 1–4:
|
|
377
|
+
|
|
378
|
+
1. create a bake against an already provisioned managed disposable device;
|
|
379
|
+
2. support only `command` and `wait_for` step types initially;
|
|
380
|
+
3. run deterministic validation plus the dedicated semantic admission call;
|
|
381
|
+
4. snapshot, execute, assert, archive on success;
|
|
382
|
+
5. rollback, start, reconnect, and return structured evidence on failure;
|
|
383
|
+
6. expose only create-bake, bake-step, bake-state, and abort/finish tools;
|
|
384
|
+
7. compile the archived steps to YAML without publishing or sharing it.
|
|
385
|
+
|
|
386
|
+
This slice deliberately postpones automatic provisioning, Codex steps, browser media,
|
|
387
|
+
sharing, and CMS publication. It proves the most valuable user experience first: the
|
|
388
|
+
AI writes ordinary logical steps while Portacode invisibly supplies the safety rails.
|
|
389
|
+
|
|
390
|
+
## Non-negotiable design rules
|
|
391
|
+
|
|
392
|
+
- Reliability is derived from independent observations, not an AI's confidence or
|
|
393
|
+
summary.
|
|
394
|
+
- Determinism means repeatable satisfaction of declared invariants for varied valid
|
|
395
|
+
inputs; it never means forcing every deployment to contain identical content.
|
|
396
|
+
- Inputs are typed, validated, redacted where necessary, and traced to the steps and
|
|
397
|
+
assertions they affect. A template is not validated if only one convenient input
|
|
398
|
+
fixture was tested.
|
|
399
|
+
- Every trial executes an immutable version from a known clean baseline.
|
|
400
|
+
- A retry after rollback is a new trial; a repair is a new template version.
|
|
401
|
+
- AI-assisted recovery during ordinary deployment must not count as template proof.
|
|
402
|
+
- Nondeterminism is declared, bounded, measured, and followed by deterministic
|
|
403
|
+
postconditions.
|
|
404
|
+
- YAML, logs, media, exports, and shared versions never contain saved secrets.
|
|
405
|
+
- Public/catalog state is an explicit promotion, never a side effect of a successful
|
|
406
|
+
private run.
|
|
407
|
+
- Long-running orchestration is durable and idempotent and respects the existing
|
|
408
|
+
WebSocket process-ownership boundary.
|
|
409
|
+
- Dashboard AI never has to manually pair snapshot, rollback, power, and reconnect
|
|
410
|
+
operations; those are atomic moderator responsibilities from its perspective.
|
|
411
|
+
- Semantic moderation informs risk and suitability, but deterministic capability and
|
|
412
|
+
authorization checks are always the enforcement boundary.
|
|
413
|
+
- Browser marketing directives are authoring metadata, not commands executed on every
|
|
414
|
+
end-user deployment.
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.2.22.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 2, 22, '
|
|
21
|
+
__version__ = version = '2.2.22.dev1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 2, 22, 'dev1')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Idempotent browser preparation for managed provisioning guests."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from .browser_runtime import MANAGED_BROWSER_CACHE, find_system_browser
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
PACKAGE_MANAGERS = ("apt", "apk", "dnf", "yum", "zypper", "pacman")
|
|
16
|
+
_SMOKE_SCRIPT = """
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from tempfile import TemporaryDirectory
|
|
19
|
+
from portacode.browser_runtime import open_browser_page
|
|
20
|
+
|
|
21
|
+
with TemporaryDirectory(prefix='portacode-browser-check-') as directory:
|
|
22
|
+
options = {'viewport': {'width': 320, 'height': 240},
|
|
23
|
+
'record_video_dir': directory,
|
|
24
|
+
'record_video_size': {'width': 320, 'height': 240}}
|
|
25
|
+
with open_browser_page(options) as (browser, context, page):
|
|
26
|
+
page.set_content('<button onclick="this.textContent=\\'Ready\\'">Test</button>')
|
|
27
|
+
page.get_by_role('button').click()
|
|
28
|
+
assert page.get_by_role('button').inner_text() == 'Ready'
|
|
29
|
+
page.wait_for_timeout(300)
|
|
30
|
+
image = Path(directory) / 'screenshot.png'
|
|
31
|
+
page.screenshot(path=str(image))
|
|
32
|
+
video = page.video
|
|
33
|
+
context.close()
|
|
34
|
+
if not image.stat().st_size or video is None or not Path(video.path()).stat().st_size:
|
|
35
|
+
raise RuntimeError('Browser screenshot/video verification failed')
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _probe_browser(environment: dict[str, str]) -> subprocess.CompletedProcess:
|
|
40
|
+
return subprocess.run(
|
|
41
|
+
[sys.executable, "-c", _SMOKE_SCRIPT], env=environment,
|
|
42
|
+
text=True, capture_output=True, timeout=120,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _require_browser(environment: dict[str, str]) -> None:
|
|
47
|
+
result = _probe_browser(environment)
|
|
48
|
+
if result.returncode:
|
|
49
|
+
detail = (result.stderr or result.stdout or "No diagnostic output")[-4000:]
|
|
50
|
+
raise RuntimeError(f"Browser screenshot/video verification failed:\n{detail}")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _install_alpine_ffmpeg(cache: Path) -> None:
|
|
54
|
+
import playwright
|
|
55
|
+
|
|
56
|
+
package = Path(playwright.__file__).resolve().parent / "driver" / "package"
|
|
57
|
+
browsers = json.loads((package / "browsers.json").read_text())["browsers"]
|
|
58
|
+
ffmpeg = next(browser for browser in browsers if browser["name"] == "ffmpeg")
|
|
59
|
+
executable = shutil.which("ffmpeg")
|
|
60
|
+
if not executable:
|
|
61
|
+
raise RuntimeError("Alpine's ffmpeg package did not provide an executable")
|
|
62
|
+
target = cache / f"ffmpeg-{ffmpeg['revision']}" / "ffmpeg-linux"
|
|
63
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
if target.is_symlink() or target.exists():
|
|
65
|
+
target.unlink()
|
|
66
|
+
target.symlink_to(executable)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def prepare_browser(package_manager: str, *, check_only: bool = False) -> None:
|
|
70
|
+
"""Install only missing guest assets, using this interpreter's Playwright."""
|
|
71
|
+
if package_manager not in PACKAGE_MANAGERS:
|
|
72
|
+
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
73
|
+
environment = {**os.environ, "PLAYWRIGHT_BROWSERS_PATH": str(MANAGED_BROWSER_CACHE)}
|
|
74
|
+
if check_only:
|
|
75
|
+
_require_browser(environment)
|
|
76
|
+
return
|
|
77
|
+
MANAGED_BROWSER_CACHE.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
def run(*command: str) -> None:
|
|
80
|
+
subprocess.run(command, env=environment, check=True)
|
|
81
|
+
|
|
82
|
+
run("chmod", "-R", "a+rX", str(MANAGED_BROWSER_CACHE))
|
|
83
|
+
if _probe_browser(environment).returncode == 0:
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if package_manager == "apt":
|
|
87
|
+
run(sys.executable, "-m", "playwright", "install", "--with-deps", "chromium", "ffmpeg")
|
|
88
|
+
elif package_manager == "apk":
|
|
89
|
+
run("apk", "add", "--no-cache", "chromium", "nss", "freetype", "harfbuzz",
|
|
90
|
+
"ca-certificates", "font-freefont", "ffmpeg")
|
|
91
|
+
_install_alpine_ffmpeg(MANAGED_BROWSER_CACHE)
|
|
92
|
+
else:
|
|
93
|
+
if not find_system_browser():
|
|
94
|
+
if package_manager in {"dnf", "yum"}:
|
|
95
|
+
release = Path("/etc/os-release").read_text().lower()
|
|
96
|
+
if "centos" in release:
|
|
97
|
+
run(package_manager, "install", "-y", "epel-release")
|
|
98
|
+
run(package_manager, "install", "-y", "chromium")
|
|
99
|
+
elif package_manager == "zypper":
|
|
100
|
+
run("zypper", "--non-interactive", "install", "-y", "chromium")
|
|
101
|
+
elif package_manager == "pacman":
|
|
102
|
+
run("pacman", "-S", "--noconfirm", "--needed", "chromium")
|
|
103
|
+
run(sys.executable, "-m", "playwright", "install", "ffmpeg")
|
|
104
|
+
run("chmod", "-R", "a+rX", str(MANAGED_BROWSER_CACHE))
|
|
105
|
+
_require_browser(environment)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def main() -> None:
|
|
109
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
110
|
+
parser.add_argument("--package-manager", required=True, choices=PACKAGE_MANAGERS)
|
|
111
|
+
parser.add_argument("--check", action="store_true")
|
|
112
|
+
arguments = parser.parse_args()
|
|
113
|
+
prepare_browser(arguments.package_manager, check_only=arguments.check)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
main()
|
|
@@ -179,12 +179,7 @@ def _install_node_if_needed() -> None:
|
|
|
179
179
|
packages = ["npm"] if node_ready else ["nodejs", "npm"]
|
|
180
180
|
_run([*_sudo_prefix(), "apk", "add", "--no-cache", *packages])
|
|
181
181
|
else:
|
|
182
|
-
|
|
183
|
-
# Exit 100 is common on Proxmox when an optional enterprise
|
|
184
|
-
# repository is unavailable; the required Ubuntu/Debian
|
|
185
|
-
# packages can still be installed from the remaining mirrors.
|
|
186
|
-
_run([*_sudo_prefix(), "apt-get", "update"], ok_returncodes=(0, 100))
|
|
187
|
-
_run([*_sudo_prefix(), "apt-get", "install", "-y", "ca-certificates", "curl"])
|
|
182
|
+
_install_linux_node_prerequisites()
|
|
188
183
|
_install_node_with_nvm()
|
|
189
184
|
elif system == "darwin":
|
|
190
185
|
_install_node_with_nvm()
|
|
@@ -210,6 +205,26 @@ def _install_node_if_needed() -> None:
|
|
|
210
205
|
raise CodexPreparationError("Node.js installation completed but npm is not available in this session.")
|
|
211
206
|
|
|
212
207
|
|
|
208
|
+
def _install_linux_node_prerequisites() -> None:
|
|
209
|
+
if shutil.which("bash") and shutil.which("curl"):
|
|
210
|
+
return
|
|
211
|
+
packages = ["ca-certificates", "bash", "curl"]
|
|
212
|
+
commands = {
|
|
213
|
+
"apt-get": ["apt-get", "install", "-y", *packages],
|
|
214
|
+
"dnf": ["dnf", "install", "-y", *packages],
|
|
215
|
+
"yum": ["yum", "install", "-y", *packages],
|
|
216
|
+
"zypper": ["zypper", "--non-interactive", "install", "-y", *packages],
|
|
217
|
+
"pacman": ["pacman", "-S", "--noconfirm", "--needed", *packages],
|
|
218
|
+
}
|
|
219
|
+
for manager, command in commands.items():
|
|
220
|
+
if shutil.which(manager):
|
|
221
|
+
if manager == "apt-get":
|
|
222
|
+
_run([*_sudo_prefix(), manager, "update"], ok_returncodes=(0, 100))
|
|
223
|
+
_run([*_sudo_prefix(), *command])
|
|
224
|
+
return
|
|
225
|
+
raise CodexPreparationError("Installing Node.js requires bash and curl; no supported package manager was found.")
|
|
226
|
+
|
|
227
|
+
|
|
213
228
|
def _install_codex() -> None:
|
|
214
229
|
existing = _codex_path()
|
|
215
230
|
if existing and _codex_works(existing):
|