portacode 1.5.14.dev2__tar.gz → 1.5.14.dev4__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.5.14.dev2 → portacode-1.5.14.dev4}/PKG-INFO +1 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/_version.py +2 -2
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/cli.py +3 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/codex_prepare.py +102 -10
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/codex_app_server.py +12 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/codex_handlers.py +90 -11
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/session.py +3 -9
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/service.py +6 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_service.py +1 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tests/test_codex_app_server.py +37 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tests/test_codex_handlers.py +21 -5
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/PKG-INFO +1 -1
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.claude/agents/communication-manager.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.claude/settings.local.json +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.gitignore +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.gitmodules +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/LICENSE +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/MANIFEST.in +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/Makefile +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/backup.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/build_android.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/connect.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/connect.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docker-compose.yaml +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/cloudflared-domain-connect-containers-audit.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/creative-team-brief-portacode.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/device-health-alerting-plan.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/devops-messaging-ab-tests.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/homepage-dashboard-positioning-fixes.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/device-transfer-button.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/device-transfer-modal.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/pair-device-button.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/pairing-request.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/student-workspace.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/template-guide.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/Dockerfile +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/docker-compose.yaml +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/Dockerfile +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/docker-compose.yaml +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/manage.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/__main__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/codex_loopback_proxy.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/client.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/automation_v2_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/base.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/chunked_content.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/cloudflare_forwarding.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/cloudflare_tunnel.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/diff_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/file_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/file_transfer_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/git_manager.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/manager.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/models.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/test_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/utils.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/proxmox_infra.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/registry.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/runtime_user.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/system_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/tab_factory.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/terminal_handlers.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_cloudflare_forwarding.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_proxmox_infra.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_runtime_user.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/update_handler.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/multiplex.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/terminal.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/test_webmin_proxy_config.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/webmin_proxy_config.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/data.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/exit_codes.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/keypair.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/elinks +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gio-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gnome-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gvfs-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/kde-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/kfmclient +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/links +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/links2 +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/lynx +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/mate-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/netsurf +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/sensible-browser +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/w3m +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/x-www-browser +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/xdg-open +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/logging_categories.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/pairing.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/restart.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/static/js/test-ntp-clock.html +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/static/js/utils/ntp-clock.js +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_runtime_paths.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_terminal_exposed_services.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/cloudflared_login.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/ensure_cloudflared.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/ensure_pyyaml.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/forwarding_state.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/get_domain.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/privileged.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/service_install.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/state.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/updater.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/diff_apply.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/diff_renderer.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/ntp_clock.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/runtime_paths.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/SOURCES.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/dependency_links.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/entry_points.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/requires.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/top_level.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/.gitignore +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/Dockerfile +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/entrypoint.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/manage_classroom.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/Dockerfile +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docker-compose.yaml +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docs/device_transfere.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docs/pairing_approval.png +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/entrypoint.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/pyproject.toml +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/restore.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/run_tests.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/scripts/diagnose_codex_gateway.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/setup.cfg +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/setup.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_device_online.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_file_operations.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_git_status_ui.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_login_flow.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_navigate_testing_folder.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_play_store_screenshots.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_buffer_performance.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_interaction.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_loading_race_condition.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_start.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_request_id.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_session_terminal_shutdown.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/.env.example +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/README.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/cli.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/__init__.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/base_test.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/cli_manager.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/hierarchical_runner.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/playwright_manager.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/runner.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/shared_cli_manager.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/test_discovery.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/requirements.txt +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/UI_UX/server_occasionally_stops_communicating_with_all_devices.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/agent_context_management.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/django_server_time_sync.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/device_performance_degradation.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/indefinite_resource_loading.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/portacode_service_silently_down.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/premature_terminal_exit.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/project_cpu_hotspots.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/terminals_exit_upon_starting.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/websocket_client_silently_dead.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/smartphone_terminal_input_frustrations.md +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/generate_play_store_assets.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/pairing_tester.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/run_screenshot_suite.sh +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/test_python_ntp_clock.py +0 -0
- {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/validate.sh +0 -0
|
@@ -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 = '1.5.14.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 5, 14, '
|
|
21
|
+
__version__ = version = '1.5.14.dev4'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 5, 14, 'dev4')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -56,7 +56,9 @@ def prepare_codex_command() -> None:
|
|
|
56
56
|
click.echo(
|
|
57
57
|
click.style(
|
|
58
58
|
"For this terminal only, run: export OPENAI_API_KEY=portacode-local\n"
|
|
59
|
-
"New
|
|
59
|
+
"New IDE terminals load /etc/portacode/codex.env automatically.\n"
|
|
60
|
+
"The Portacode systemd service loads that file via EnvironmentFile "
|
|
61
|
+
"(restart the service once if this device was prepared before that support).",
|
|
60
62
|
fg="yellow",
|
|
61
63
|
)
|
|
62
64
|
)
|
|
@@ -6,13 +6,14 @@ import json
|
|
|
6
6
|
import os
|
|
7
7
|
import platform
|
|
8
8
|
import re
|
|
9
|
+
import shlex
|
|
9
10
|
import shutil
|
|
10
11
|
import subprocess
|
|
11
12
|
import sys
|
|
12
13
|
import urllib.error
|
|
13
14
|
import urllib.request
|
|
14
15
|
from pathlib import Path
|
|
15
|
-
from typing import Iterable
|
|
16
|
+
from typing import Dict, Iterable, Mapping, MutableMapping, Optional
|
|
16
17
|
|
|
17
18
|
from .codex_loopback_proxy import CODEX_LOOPBACK_HOST, CODEX_LOOPBACK_PORT
|
|
18
19
|
|
|
@@ -30,6 +31,8 @@ env_key = "OPENAI_API_KEY"
|
|
|
30
31
|
'''
|
|
31
32
|
|
|
32
33
|
LOCAL_SENTINEL = "portacode-local"
|
|
34
|
+
CODEX_ENV_PATH = Path("/etc/portacode/codex.env")
|
|
35
|
+
OPENAI_API_KEY_ENV = "OPENAI_API_KEY"
|
|
33
36
|
|
|
34
37
|
|
|
35
38
|
class CodexPreparationError(RuntimeError):
|
|
@@ -142,32 +145,121 @@ def _write_config() -> Path:
|
|
|
142
145
|
return config_path
|
|
143
146
|
|
|
144
147
|
|
|
148
|
+
def read_codex_env_file(path: Optional[Path] = None) -> Dict[str, str]:
|
|
149
|
+
"""Parse KEY=VALUE lines from the managed Codex env file."""
|
|
150
|
+
path = path or CODEX_ENV_PATH
|
|
151
|
+
values: Dict[str, str] = {}
|
|
152
|
+
try:
|
|
153
|
+
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
154
|
+
line = raw_line.strip()
|
|
155
|
+
if not line or line.startswith("#"):
|
|
156
|
+
continue
|
|
157
|
+
key, separator, value = line.partition("=")
|
|
158
|
+
if separator and key:
|
|
159
|
+
values[key] = value
|
|
160
|
+
except OSError:
|
|
161
|
+
pass
|
|
162
|
+
return values
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def apply_codex_env_to_mapping(
|
|
166
|
+
env: MutableMapping[str, str],
|
|
167
|
+
path: Optional[Path] = None,
|
|
168
|
+
) -> None:
|
|
169
|
+
"""Ensure OPENAI_API_KEY (and other managed vars) are present for child processes.
|
|
170
|
+
|
|
171
|
+
Shell profiles / IDE terminals already source ``/etc/portacode/codex.env``.
|
|
172
|
+
The long-running Portacode systemd service does not, so callers that spawn
|
|
173
|
+
Codex (or interactive shells) must merge this file explicitly.
|
|
174
|
+
"""
|
|
175
|
+
file_values = read_codex_env_file(path)
|
|
176
|
+
for key, value in file_values.items():
|
|
177
|
+
if value:
|
|
178
|
+
env[key] = value
|
|
179
|
+
# Always guarantee the local proxy sentinel unless a non-empty value exists.
|
|
180
|
+
if not (env.get(OPENAI_API_KEY_ENV) or "").strip():
|
|
181
|
+
env[OPENAI_API_KEY_ENV] = file_values.get(OPENAI_API_KEY_ENV) or LOCAL_SENTINEL
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def build_codex_subprocess_env(
|
|
185
|
+
base: Optional[Mapping[str, str]] = None,
|
|
186
|
+
path: Optional[Path] = None,
|
|
187
|
+
) -> Dict[str, str]:
|
|
188
|
+
"""Environment for Codex CLI / app-server subprocesses."""
|
|
189
|
+
env = dict(base or os.environ)
|
|
190
|
+
apply_codex_env_to_mapping(env, path=path)
|
|
191
|
+
return env
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _install_systemd_codex_environment_file() -> None:
|
|
195
|
+
"""Make Portacode's systemd unit load ``/etc/portacode/codex.env`` on start.
|
|
196
|
+
|
|
197
|
+
systemd does not source ``/etc/profile.d`` or ``/etc/environment`` for
|
|
198
|
+
``Type=simple`` services the way login shells do. A drop-in EnvironmentFile
|
|
199
|
+
is the reliable propagation path for the agent process itself.
|
|
200
|
+
"""
|
|
201
|
+
dropin_body = (
|
|
202
|
+
"# Managed by portacode prepare codex.\n"
|
|
203
|
+
"[Service]\n"
|
|
204
|
+
f"EnvironmentFile=-{CODEX_ENV_PATH}\n"
|
|
205
|
+
)
|
|
206
|
+
system_unit = Path("/etc/systemd/system/portacode.service")
|
|
207
|
+
user_unit = Path.home() / ".config/systemd/user/portacode.service"
|
|
208
|
+
|
|
209
|
+
if system_unit.exists():
|
|
210
|
+
dropin_dir = Path("/etc/systemd/system/portacode.service.d")
|
|
211
|
+
dropin_path = dropin_dir / "codex.conf"
|
|
212
|
+
script = (
|
|
213
|
+
f"install -d -m 755 {shlex.quote(str(dropin_dir))} && "
|
|
214
|
+
f"printf '%s' {shlex.quote(dropin_body)} > {shlex.quote(str(dropin_path))} && "
|
|
215
|
+
f"chmod 644 {shlex.quote(str(dropin_path))}"
|
|
216
|
+
)
|
|
217
|
+
_run([*_sudo_prefix(), "sh", "-c", script])
|
|
218
|
+
subprocess.run([*_sudo_prefix(), "systemctl", "daemon-reload"], check=False)
|
|
219
|
+
return
|
|
220
|
+
|
|
221
|
+
if user_unit.exists():
|
|
222
|
+
dropin_dir = Path.home() / ".config/systemd/user/portacode.service.d"
|
|
223
|
+
dropin_dir.mkdir(parents=True, exist_ok=True)
|
|
224
|
+
dropin_path = dropin_dir / "codex.conf"
|
|
225
|
+
dropin_path.write_text(dropin_body, encoding="utf-8")
|
|
226
|
+
dropin_path.chmod(0o644)
|
|
227
|
+
subprocess.run(["systemctl", "--user", "daemon-reload"], check=False)
|
|
228
|
+
|
|
229
|
+
|
|
145
230
|
def _set_local_sentinel() -> None:
|
|
146
231
|
system = platform.system().lower()
|
|
147
|
-
os.environ[
|
|
232
|
+
os.environ[OPENAI_API_KEY_ENV] = LOCAL_SENTINEL
|
|
148
233
|
if system == "linux":
|
|
149
234
|
# Portacode terminals load this directly, including sessions started by
|
|
150
235
|
# a long-running agent that pre-dates the prepare command.
|
|
151
236
|
managed_setup = (
|
|
152
237
|
"install -d -m 755 /etc/portacode && "
|
|
153
|
-
"printf '%s\\n' '
|
|
154
|
-
"chmod 644
|
|
238
|
+
f"printf '%s\\n' '{OPENAI_API_KEY_ENV}={LOCAL_SENTINEL}' > {CODEX_ENV_PATH} && "
|
|
239
|
+
f"chmod 644 {CODEX_ENV_PATH} && "
|
|
155
240
|
"printf '%s\\n' '# Managed by portacode prepare codex.' "
|
|
156
|
-
"'if [ -r
|
|
157
|
-
"' .
|
|
241
|
+
f"'if [ -r {CODEX_ENV_PATH} ]; then' ' set -a' "
|
|
242
|
+
f"' . {CODEX_ENV_PATH}' ' set +a' 'fi' > /etc/profile.d/portacode_codex.sh && "
|
|
158
243
|
"chmod 644 /etc/profile.d/portacode_codex.sh"
|
|
159
244
|
)
|
|
160
245
|
_run([*_sudo_prefix(), "sh", "-c", managed_setup])
|
|
161
|
-
_run([
|
|
246
|
+
_run([
|
|
247
|
+
*_sudo_prefix(),
|
|
248
|
+
"sh",
|
|
249
|
+
"-c",
|
|
250
|
+
f'grep -q "^{OPENAI_API_KEY_ENV}={LOCAL_SENTINEL}$" /etc/environment || '
|
|
251
|
+
f'printf "%s\\n" "{OPENAI_API_KEY_ENV}={LOCAL_SENTINEL}" >> /etc/environment',
|
|
252
|
+
])
|
|
253
|
+
_install_systemd_codex_environment_file()
|
|
162
254
|
elif system == "darwin":
|
|
163
255
|
zshenv = Path.home() / ".zshenv"
|
|
164
|
-
line = "export
|
|
256
|
+
line = f"export {OPENAI_API_KEY_ENV}={LOCAL_SENTINEL}\n"
|
|
165
257
|
if not zshenv.exists() or line not in zshenv.read_text(encoding="utf-8", errors="ignore"):
|
|
166
258
|
with zshenv.open("a", encoding="utf-8") as handle:
|
|
167
259
|
handle.write(line)
|
|
168
|
-
subprocess.run(["launchctl", "setenv",
|
|
260
|
+
subprocess.run(["launchctl", "setenv", OPENAI_API_KEY_ENV, LOCAL_SENTINEL], check=False)
|
|
169
261
|
elif system == "windows":
|
|
170
|
-
_run(["setx",
|
|
262
|
+
_run(["setx", OPENAI_API_KEY_ENV, LOCAL_SENTINEL])
|
|
171
263
|
|
|
172
264
|
|
|
173
265
|
def _verify_loopback_proxy() -> None:
|
|
@@ -79,6 +79,10 @@ class CodexAppServer:
|
|
|
79
79
|
f"{self.command[0]} not found on PATH. Run `portacode prepare codex` on this device."
|
|
80
80
|
)
|
|
81
81
|
|
|
82
|
+
# systemd services do not load shell profile env; merge /etc/portacode/codex.env.
|
|
83
|
+
from portacode.codex_prepare import build_codex_subprocess_env
|
|
84
|
+
|
|
85
|
+
child_env = build_codex_subprocess_env()
|
|
82
86
|
LOGGER.info("Spawning Codex app-server: %s", " ".join(self.command))
|
|
83
87
|
try:
|
|
84
88
|
self._proc = await self._process_factory(
|
|
@@ -86,7 +90,7 @@ class CodexAppServer:
|
|
|
86
90
|
stdin=asyncio.subprocess.PIPE,
|
|
87
91
|
stdout=asyncio.subprocess.PIPE,
|
|
88
92
|
stderr=asyncio.subprocess.PIPE,
|
|
89
|
-
env=
|
|
93
|
+
env=child_env,
|
|
90
94
|
)
|
|
91
95
|
except Exception as exc:
|
|
92
96
|
raise CodexAppServerError(f"Failed to spawn codex app-server: {exc}") from exc
|
|
@@ -96,6 +100,13 @@ class CodexAppServer:
|
|
|
96
100
|
|
|
97
101
|
await self._initialize()
|
|
98
102
|
|
|
103
|
+
async def recycle(self) -> None:
|
|
104
|
+
"""Stop a running app-server so the next ``start()`` respawns with fresh env."""
|
|
105
|
+
async with self._lock:
|
|
106
|
+
await self._kill_locked()
|
|
107
|
+
self._closed = False
|
|
108
|
+
self._initialized.clear()
|
|
109
|
+
|
|
99
110
|
async def _kill_locked(self) -> None:
|
|
100
111
|
"""Tear down the subprocess and reader tasks."""
|
|
101
112
|
for future in list(self._pending.values()):
|
{portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/codex_handlers.py
RENAMED
|
@@ -86,6 +86,71 @@ def _project_id(message: Dict[str, Any]) -> Optional[str]:
|
|
|
86
86
|
return message.get("project_id")
|
|
87
87
|
|
|
88
88
|
|
|
89
|
+
def _thread_from_result(result: Dict[str, Any]) -> Dict[str, Any]:
|
|
90
|
+
"""Normalize thread/start|resume|read results to a thread object.
|
|
91
|
+
|
|
92
|
+
Current app-server returns `{ "thread": { "id": ... } }`. Older fakes /
|
|
93
|
+
builds may return the thread fields at the top level.
|
|
94
|
+
"""
|
|
95
|
+
if not isinstance(result, dict):
|
|
96
|
+
return {}
|
|
97
|
+
thread = result.get("thread")
|
|
98
|
+
if isinstance(thread, dict):
|
|
99
|
+
return thread
|
|
100
|
+
return result
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _thread_id_from_result(result: Dict[str, Any]) -> Optional[str]:
|
|
104
|
+
thread = _thread_from_result(result)
|
|
105
|
+
return thread.get("id") or result.get("id") or result.get("threadId")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _threads_from_list_result(result: Dict[str, Any]) -> tuple[list, Optional[str]]:
|
|
109
|
+
"""Normalize thread/list results.
|
|
110
|
+
|
|
111
|
+
Current app-server returns `{ "data": [...], "nextCursor": ... }`.
|
|
112
|
+
"""
|
|
113
|
+
if not isinstance(result, dict):
|
|
114
|
+
return [], None
|
|
115
|
+
threads = result.get("data")
|
|
116
|
+
if threads is None:
|
|
117
|
+
threads = result.get("threads")
|
|
118
|
+
if not isinstance(threads, list):
|
|
119
|
+
threads = []
|
|
120
|
+
return threads, result.get("nextCursor")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _items_from_read_result(result: Dict[str, Any]) -> list:
|
|
124
|
+
"""Extract conversation items from thread/read (or legacy shapes)."""
|
|
125
|
+
if not isinstance(result, dict):
|
|
126
|
+
return []
|
|
127
|
+
if isinstance(result.get("items"), list):
|
|
128
|
+
return result["items"]
|
|
129
|
+
if isinstance(result.get("messages"), list):
|
|
130
|
+
return result["messages"]
|
|
131
|
+
|
|
132
|
+
thread = _thread_from_result(result)
|
|
133
|
+
turns = thread.get("turns") or result.get("turns") or []
|
|
134
|
+
items: list = []
|
|
135
|
+
if isinstance(turns, list):
|
|
136
|
+
for turn in turns:
|
|
137
|
+
if not isinstance(turn, dict):
|
|
138
|
+
continue
|
|
139
|
+
turn_items = turn.get("items")
|
|
140
|
+
if isinstance(turn_items, list):
|
|
141
|
+
items.extend(turn_items)
|
|
142
|
+
return items
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _turn_from_result(result: Dict[str, Any]) -> Dict[str, Any]:
|
|
146
|
+
if not isinstance(result, dict):
|
|
147
|
+
return {}
|
|
148
|
+
turn = result.get("turn")
|
|
149
|
+
if isinstance(turn, dict):
|
|
150
|
+
return turn
|
|
151
|
+
return result
|
|
152
|
+
|
|
153
|
+
|
|
89
154
|
class CodexAsyncHandler(AsyncHandler):
|
|
90
155
|
"""AsyncHandler that replies to `source_client_session` per WEBSOCKET_PROTOCOL.md."""
|
|
91
156
|
|
|
@@ -173,19 +238,23 @@ class CodexThreadListHandler(CodexAsyncHandler):
|
|
|
173
238
|
manager = _get_manager(self)
|
|
174
239
|
await manager.ensure_started()
|
|
175
240
|
|
|
176
|
-
|
|
177
|
-
|
|
241
|
+
# Default sourceKinds is cli+vscode only; include appServer so IDE chats appear.
|
|
242
|
+
# Avoid useStateDbOnly:true — it has returned empty lists in some Codex builds.
|
|
243
|
+
params: Dict[str, Any] = {
|
|
244
|
+
"cwd": cwd,
|
|
245
|
+
"sourceKinds": ["cli", "vscode", "appServer"],
|
|
246
|
+
"limit": message.get("limit") if message.get("limit") is not None else 50,
|
|
247
|
+
}
|
|
178
248
|
cursor = message.get("cursor")
|
|
179
|
-
if limit is not None:
|
|
180
|
-
params["limit"] = limit
|
|
181
249
|
if cursor is not None:
|
|
182
250
|
params["cursor"] = cursor
|
|
183
251
|
|
|
184
252
|
result = await manager.bridge.call("thread/list", params)
|
|
253
|
+
threads, next_cursor = _threads_from_list_result(result)
|
|
185
254
|
payload = {
|
|
186
255
|
"event": "codex_thread_list",
|
|
187
|
-
"threads":
|
|
188
|
-
"nextCursor":
|
|
256
|
+
"threads": threads,
|
|
257
|
+
"nextCursor": next_cursor,
|
|
189
258
|
}
|
|
190
259
|
project_id = _project_id(message)
|
|
191
260
|
if project_id:
|
|
@@ -217,7 +286,8 @@ class CodexThreadStartHandler(CodexAsyncHandler):
|
|
|
217
286
|
params["modelProvider"] = message["modelProvider"]
|
|
218
287
|
|
|
219
288
|
result = await manager.bridge.call("thread/start", params)
|
|
220
|
-
|
|
289
|
+
thread = _thread_from_result(result)
|
|
290
|
+
thread_id = _thread_id_from_result(result)
|
|
221
291
|
if not thread_id:
|
|
222
292
|
raise CodexAppServerError("thread/start did not return a thread id")
|
|
223
293
|
|
|
@@ -226,7 +296,7 @@ class CodexThreadStartHandler(CodexAsyncHandler):
|
|
|
226
296
|
payload = {
|
|
227
297
|
"event": "codex_thread_started",
|
|
228
298
|
"threadId": thread_id,
|
|
229
|
-
"thread":
|
|
299
|
+
"thread": thread,
|
|
230
300
|
}
|
|
231
301
|
if project_id:
|
|
232
302
|
payload["project_id"] = project_id
|
|
@@ -255,8 +325,11 @@ class CodexThreadResumeHandler(CodexAsyncHandler):
|
|
|
255
325
|
resume_params["cwd"] = cwd
|
|
256
326
|
await manager.bridge.call("thread/resume", resume_params)
|
|
257
327
|
|
|
258
|
-
read_result = await manager.bridge.call(
|
|
259
|
-
|
|
328
|
+
read_result = await manager.bridge.call(
|
|
329
|
+
"thread/read",
|
|
330
|
+
{"threadId": thread_id, "includeTurns": True},
|
|
331
|
+
)
|
|
332
|
+
items = _items_from_read_result(read_result)
|
|
260
333
|
|
|
261
334
|
if cwd and project_id:
|
|
262
335
|
manager.record_thread(thread_id, cwd, project_id)
|
|
@@ -305,7 +378,7 @@ class CodexTurnStartHandler(CodexAsyncHandler):
|
|
|
305
378
|
payload = {
|
|
306
379
|
"event": "codex_turn_started",
|
|
307
380
|
"threadId": thread_id,
|
|
308
|
-
"turn": result,
|
|
381
|
+
"turn": _turn_from_result(result),
|
|
309
382
|
}
|
|
310
383
|
if project_id:
|
|
311
384
|
payload["project_id"] = project_id
|
|
@@ -371,6 +444,12 @@ class CodexPrepareHandler(CodexAsyncHandler):
|
|
|
371
444
|
try:
|
|
372
445
|
loop = asyncio.get_running_loop()
|
|
373
446
|
await loop.run_in_executor(None, prepare_codex)
|
|
447
|
+
# Drop any app-server started before the sentinel was available so
|
|
448
|
+
# the next chat command respawns with OPENAI_API_KEY injected.
|
|
449
|
+
try:
|
|
450
|
+
await manager.bridge.recycle()
|
|
451
|
+
except Exception:
|
|
452
|
+
LOGGER.exception("Failed to recycle Codex app-server after prepare")
|
|
374
453
|
payload = {"event": "codex_prepare_done", "success": True}
|
|
375
454
|
except CodexPreparationError as exc:
|
|
376
455
|
LOGGER.warning("codex_prepare failed: %s", exc)
|
|
@@ -15,6 +15,7 @@ from typing import Any, Awaitable, Callable, Dict, Optional, List, TYPE_CHECKING
|
|
|
15
15
|
|
|
16
16
|
from platformdirs import user_data_dir
|
|
17
17
|
|
|
18
|
+
from portacode.codex_prepare import CODEX_ENV_PATH, apply_codex_env_to_mapping
|
|
18
19
|
from portacode.link_capture import prepare_link_capture_bin
|
|
19
20
|
from .runtime_user import get_default_runtime_user, wrap_argv_for_user
|
|
20
21
|
|
|
@@ -38,7 +39,6 @@ _LINK_EVENT_ROOT = Path(user_data_dir("portacode", "portacode")) / "link_events"
|
|
|
38
39
|
_LINK_EVENT_POLL_INTERVAL = 0.5 # seconds
|
|
39
40
|
LINK_EVENT_THROTTLE_SECONDS = 5.0
|
|
40
41
|
LINK_CAPTURE_ORIGINAL_BROWSER_ENV = "PORTACODE_LINK_CAPTURE_ORIGINAL_BROWSER"
|
|
41
|
-
CODEX_ENV_PATH = Path("/etc/portacode/codex.env")
|
|
42
42
|
|
|
43
43
|
logger = logging.getLogger(__name__)
|
|
44
44
|
|
|
@@ -72,14 +72,8 @@ _DEFAULT_ENV = {
|
|
|
72
72
|
def _build_child_env() -> Dict[str, str]:
|
|
73
73
|
"""Return a copy of os.environ with sensible fallbacks and managed vars."""
|
|
74
74
|
env = os.environ.copy()
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
key, separator, value = raw_line.partition("=")
|
|
78
|
-
if separator and key == "OPENAI_API_KEY" and value:
|
|
79
|
-
# The file is managed locally by ``portacode prepare codex``.
|
|
80
|
-
env[key] = value
|
|
81
|
-
except OSError:
|
|
82
|
-
pass
|
|
75
|
+
# Managed by ``portacode prepare codex`` (/etc/portacode/codex.env).
|
|
76
|
+
apply_codex_env_to_mapping(env)
|
|
83
77
|
for k, v in _DEFAULT_ENV.items():
|
|
84
78
|
env.setdefault(k, v)
|
|
85
79
|
env.setdefault("COLUMNS", str(TERMINAL_COLUMNS))
|
|
@@ -173,7 +173,12 @@ class _SystemdUserService:
|
|
|
173
173
|
|
|
174
174
|
# Capture current SHELL for the service to prevent using /bin/sh in containers/virtualized environments
|
|
175
175
|
current_shell = os.getenv("SHELL", "/bin/bash")
|
|
176
|
-
env_lines = [
|
|
176
|
+
env_lines = [
|
|
177
|
+
f"Environment=SHELL={current_shell}",
|
|
178
|
+
# Optional until `portacode prepare codex` creates the file. Ensures the
|
|
179
|
+
# long-running agent (and Codex app-server children) see OPENAI_API_KEY.
|
|
180
|
+
"EnvironmentFile=-/etc/portacode/codex.env",
|
|
181
|
+
]
|
|
177
182
|
if self.system_mode:
|
|
178
183
|
env_lines.append(f"Environment=PORTACODE_DEFAULT_RUNTIME_USER={self.runtime_user}")
|
|
179
184
|
if self.xdg_data_home:
|
|
@@ -49,6 +49,7 @@ class ServiceCommandTests(TestCase):
|
|
|
49
49
|
|
|
50
50
|
unit_text = service.service_path.read_text(encoding="utf-8")
|
|
51
51
|
self.assertIn("ExecStart=/opt/portacode-venv/bin/python -m portacode connect --non-interactive", unit_text)
|
|
52
|
+
self.assertIn("EnvironmentFile=-/etc/portacode/codex.env", unit_text)
|
|
52
53
|
self.assertIn("--project-path '$$HOME/.openclaw'", unit_text)
|
|
53
54
|
self.assertIn("--project-path '$$HOME/.openclaw/workspace'", unit_text)
|
|
54
55
|
self.assertLess(unit_text.index("$$HOME/.openclaw"), unit_text.index("$$HOME/.openclaw/workspace"))
|
|
@@ -93,9 +93,11 @@ def _factory_for(*processes: FakeProcess):
|
|
|
93
93
|
|
|
94
94
|
async def _factory(*_args, **_kwargs):
|
|
95
95
|
try:
|
|
96
|
-
|
|
96
|
+
proc = next(proc_iter)
|
|
97
97
|
except StopIteration as exc:
|
|
98
98
|
raise RuntimeError("No more fake processes") from exc
|
|
99
|
+
proc.spawn_kwargs = _kwargs
|
|
100
|
+
return proc
|
|
99
101
|
|
|
100
102
|
return _factory
|
|
101
103
|
|
|
@@ -111,6 +113,40 @@ async def test_initialize_handshake():
|
|
|
111
113
|
await bridge.stop()
|
|
112
114
|
|
|
113
115
|
|
|
116
|
+
@pytest.mark.asyncio
|
|
117
|
+
async def test_spawn_injects_openai_api_key(tmp_path, monkeypatch):
|
|
118
|
+
env_file = tmp_path / "codex.env"
|
|
119
|
+
env_file.write_text("OPENAI_API_KEY=portacode-local\n", encoding="utf-8")
|
|
120
|
+
monkeypatch.setattr("portacode.codex_prepare.CODEX_ENV_PATH", env_file)
|
|
121
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
122
|
+
|
|
123
|
+
proc = FakeProcess([{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "v2"}}])
|
|
124
|
+
bridge = CodexAppServer(_process_factory=_factory_for(proc))
|
|
125
|
+
await bridge.start()
|
|
126
|
+
|
|
127
|
+
env = getattr(proc, "spawn_kwargs", {}).get("env") or {}
|
|
128
|
+
assert env.get("OPENAI_API_KEY") == "portacode-local"
|
|
129
|
+
await bridge.stop()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_build_codex_subprocess_env_reads_managed_file(tmp_path):
|
|
133
|
+
from portacode.codex_prepare import build_codex_subprocess_env
|
|
134
|
+
|
|
135
|
+
env_file = tmp_path / "codex.env"
|
|
136
|
+
env_file.write_text("OPENAI_API_KEY=portacode-local\n", encoding="utf-8")
|
|
137
|
+
env = build_codex_subprocess_env(base={"PATH": "/usr/bin"}, path=env_file)
|
|
138
|
+
assert env["OPENAI_API_KEY"] == "portacode-local"
|
|
139
|
+
assert env["PATH"] == "/usr/bin"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_build_codex_subprocess_env_defaults_sentinel_without_file(tmp_path):
|
|
143
|
+
from portacode.codex_prepare import LOCAL_SENTINEL, build_codex_subprocess_env
|
|
144
|
+
|
|
145
|
+
missing = tmp_path / "missing.env"
|
|
146
|
+
env = build_codex_subprocess_env(base={}, path=missing)
|
|
147
|
+
assert env["OPENAI_API_KEY"] == LOCAL_SENTINEL
|
|
148
|
+
|
|
149
|
+
|
|
114
150
|
@pytest.mark.asyncio
|
|
115
151
|
async def test_request_response_correlation():
|
|
116
152
|
# Only preload the initialize response. Queuing the call response early can
|
|
@@ -43,11 +43,20 @@ class FakeBridge:
|
|
|
43
43
|
def __init__(self) -> None:
|
|
44
44
|
self.calls: list[tuple[str, dict]] = []
|
|
45
45
|
self.responses: dict[str, Any] = {
|
|
46
|
-
|
|
47
|
-
"thread/
|
|
48
|
-
"thread/
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
# Match current Codex app-server response shapes.
|
|
47
|
+
"thread/start": {"thread": {"id": "th-1", "name": "New chat"}},
|
|
48
|
+
"thread/list": {
|
|
49
|
+
"data": [{"id": "th-1", "name": "Existing chat", "preview": "hi"}],
|
|
50
|
+
"nextCursor": None,
|
|
51
|
+
},
|
|
52
|
+
"thread/resume": {"thread": {"id": "th-1"}},
|
|
53
|
+
"thread/read": {
|
|
54
|
+
"thread": {
|
|
55
|
+
"id": "th-1",
|
|
56
|
+
"turns": [{"id": "turn-1", "items": [{"id": "msg-1", "role": "user", "text": "hello"}]}],
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"turn/start": {"turn": {"id": "turn-1", "threadId": "th-1"}},
|
|
51
60
|
"turn/interrupt": {},
|
|
52
61
|
}
|
|
53
62
|
|
|
@@ -103,6 +112,11 @@ async def test_codex_thread_list():
|
|
|
103
112
|
assert payload["event"] == "codex_thread_list"
|
|
104
113
|
assert payload["threads"][0]["id"] == "th-1"
|
|
105
114
|
assert payload["project_id"] == "p-1"
|
|
115
|
+
list_call = manager.bridge.calls[-1]
|
|
116
|
+
assert list_call[0] == "thread/list"
|
|
117
|
+
assert list_call[1]["cwd"] == "/tmp/proj"
|
|
118
|
+
assert "appServer" in list_call[1]["sourceKinds"]
|
|
119
|
+
assert "useStateDbOnly" not in list_call[1]
|
|
106
120
|
|
|
107
121
|
|
|
108
122
|
@pytest.mark.asyncio
|
|
@@ -138,6 +152,8 @@ async def test_codex_thread_resume():
|
|
|
138
152
|
assert payload["event"] == "codex_thread_resumed"
|
|
139
153
|
assert payload["items"][0]["text"] == "hello"
|
|
140
154
|
assert manager._thread_project["th-1"] == "p-1"
|
|
155
|
+
read_call = next(c for c in manager.bridge.calls if c[0] == "thread/read")
|
|
156
|
+
assert read_call[1]["includeTurns"] is True
|
|
141
157
|
|
|
142
158
|
|
|
143
159
|
@pytest.mark.asyncio
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/cloudflared-domain-connect-containers-audit.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/homepage-dashboard-positioning-fixes.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|