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.
Files changed (437) hide show
  1. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/PKG-INFO +1 -1
  2. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/_version.py +2 -2
  3. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/cli.py +3 -1
  4. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/codex_prepare.py +102 -10
  5. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/codex_app_server.py +12 -1
  6. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/codex_handlers.py +90 -11
  7. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/session.py +3 -9
  8. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/service.py +6 -1
  9. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_service.py +1 -0
  10. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tests/test_codex_app_server.py +37 -1
  11. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tests/test_codex_handlers.py +21 -5
  12. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/PKG-INFO +1 -1
  13. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.claude/agents/communication-manager.md +0 -0
  14. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.claude/settings.local.json +0 -0
  15. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.gitignore +0 -0
  16. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/.gitmodules +0 -0
  17. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/LICENSE +0 -0
  18. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/MANIFEST.in +0 -0
  19. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/Makefile +0 -0
  20. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/README.md +0 -0
  21. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/backup.sh +0 -0
  22. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/build_android.sh +0 -0
  23. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/connect.py +0 -0
  24. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/connect.sh +0 -0
  25. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docker-compose.yaml +0 -0
  26. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/cloudflared-domain-connect-containers-audit.md +0 -0
  27. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/creative-team-brief-portacode.md +0 -0
  28. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/device-health-alerting-plan.md +0 -0
  29. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/devops-messaging-ab-tests.md +0 -0
  30. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/homepage-dashboard-positioning-fixes.md +0 -0
  31. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/device-transfer-button.png +0 -0
  32. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/device-transfer-modal.png +0 -0
  33. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/pair-device-button.png +0 -0
  34. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/pairing-request.png +0 -0
  35. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/images/student-workspace.png +0 -0
  36. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/docs/template-guide.html +0 -0
  37. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/README.md +0 -0
  38. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/Dockerfile +0 -0
  39. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/README.md +0 -0
  40. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
  41. {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
  42. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/simple_device/docker-compose.yaml +0 -0
  43. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/Dockerfile +0 -0
  44. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/README.md +0 -0
  45. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
  46. {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
  47. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
  48. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
  49. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
  50. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
  51. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
  52. {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
  53. {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
  54. {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
  55. {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
  56. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
  57. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
  58. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
  59. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
  60. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
  61. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
  62. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
  63. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
  64. {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
  65. {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
  66. {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
  67. {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
  68. {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
  69. {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
  70. {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
  71. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
  72. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
  73. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
  74. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
  75. {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
  76. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
  77. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
  78. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
  79. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
  80. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
  81. {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
  82. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
  83. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
  84. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
  85. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
  86. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
  87. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
  88. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
  89. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
  90. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
  91. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
  92. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
  93. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
  94. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
  95. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
  96. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
  97. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
  98. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
  99. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
  100. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
  101. {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
  102. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
  103. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
  104. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
  105. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
  106. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
  107. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
  108. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
  109. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
  110. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
  111. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
  112. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
  113. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
  114. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
  115. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
  116. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
  117. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
  118. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
  119. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
  120. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
  121. {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
  122. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
  123. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
  124. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
  125. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
  126. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
  127. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
  128. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
  129. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
  130. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
  131. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
  132. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
  133. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
  134. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
  135. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
  136. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
  137. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
  138. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
  139. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
  140. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
  141. {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
  142. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
  143. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
  144. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
  145. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
  146. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
  147. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
  148. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
  149. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
  150. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
  151. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
  152. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
  153. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
  154. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
  155. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
  156. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
  157. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
  158. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
  159. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
  160. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
  161. {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
  162. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
  163. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
  164. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
  165. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
  166. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
  167. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
  168. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
  169. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
  170. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
  171. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
  172. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
  173. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
  174. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
  175. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
  176. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
  177. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
  178. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
  179. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
  180. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
  181. {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
  182. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
  183. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
  184. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
  185. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
  186. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
  187. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
  188. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
  189. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
  190. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
  191. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
  192. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
  193. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
  194. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
  195. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
  196. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
  197. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
  198. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
  199. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
  200. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
  201. {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
  202. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
  203. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
  204. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
  205. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
  206. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
  207. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
  208. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
  209. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
  210. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
  211. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
  212. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
  213. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
  214. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
  215. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
  216. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
  217. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
  218. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
  219. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
  220. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
  221. {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
  222. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
  223. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
  224. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
  225. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
  226. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
  227. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
  228. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
  229. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
  230. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
  231. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
  232. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
  233. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
  234. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
  235. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
  236. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
  237. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
  238. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
  239. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
  240. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
  241. {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
  242. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
  243. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
  244. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
  245. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
  246. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
  247. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
  248. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
  249. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
  250. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
  251. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
  252. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
  253. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
  254. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
  255. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
  256. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
  257. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
  258. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
  259. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
  260. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/docker-compose.yaml +0 -0
  261. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/README.md +0 -0
  262. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
  263. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
  264. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
  265. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
  266. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
  267. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/manage.py +0 -0
  268. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
  269. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
  270. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
  271. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
  272. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
  273. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
  274. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
  275. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
  276. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
  277. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
  278. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
  279. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
  280. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/README.md +0 -0
  281. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/__init__.py +0 -0
  282. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/__main__.py +0 -0
  283. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/codex_loopback_proxy.py +0 -0
  284. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/README.md +0 -0
  285. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/__init__.py +0 -0
  286. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/client.py +0 -0
  287. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/README.md +0 -0
  288. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
  289. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/__init__.py +0 -0
  290. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/automation_v2_handlers.py +0 -0
  291. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/base.py +0 -0
  292. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/chunked_content.py +0 -0
  293. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/cloudflare_forwarding.py +0 -0
  294. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/cloudflare_tunnel.py +0 -0
  295. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/diff_handlers.py +0 -0
  296. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/file_handlers.py +0 -0
  297. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/file_transfer_handlers.py +0 -0
  298. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
  299. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/README.md +0 -0
  300. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/__init__.py +0 -0
  301. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
  302. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/git_manager.py +0 -0
  303. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/handlers.py +0 -0
  304. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/manager.py +0 -0
  305. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/models.py +0 -0
  306. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/test_handlers.py +0 -0
  307. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state/utils.py +0 -0
  308. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/project_state_handlers.py +0 -0
  309. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/proxmox_infra.py +0 -0
  310. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/registry.py +0 -0
  311. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/runtime_user.py +0 -0
  312. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/system_handlers.py +0 -0
  313. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/tab_factory.py +0 -0
  314. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/terminal_handlers.py +0 -0
  315. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_cloudflare_forwarding.py +0 -0
  316. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_proxmox_infra.py +0 -0
  317. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/test_runtime_user.py +0 -0
  318. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/handlers/update_handler.py +0 -0
  319. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/multiplex.py +0 -0
  320. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/terminal.py +0 -0
  321. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/test_webmin_proxy_config.py +0 -0
  322. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/connection/webmin_proxy_config.py +0 -0
  323. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/data.py +0 -0
  324. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/exit_codes.py +0 -0
  325. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/keypair.py +0 -0
  326. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/__init__.py +0 -0
  327. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  328. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/elinks +0 -0
  329. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gio-open +0 -0
  330. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gnome-open +0 -0
  331. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/gvfs-open +0 -0
  332. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/kde-open +0 -0
  333. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/kfmclient +0 -0
  334. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
  335. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
  336. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/links +0 -0
  337. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/links2 +0 -0
  338. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/lynx +0 -0
  339. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/mate-open +0 -0
  340. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/netsurf +0 -0
  341. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/sensible-browser +0 -0
  342. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/w3m +0 -0
  343. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/x-www-browser +0 -0
  344. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/link_capture/bin/xdg-open +0 -0
  345. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/logging_categories.py +0 -0
  346. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/pairing.py +0 -0
  347. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/restart.py +0 -0
  348. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/static/js/test-ntp-clock.html +0 -0
  349. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/static/js/utils/ntp-clock.js +0 -0
  350. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_runtime_paths.py +0 -0
  351. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/test_terminal_exposed_services.py +0 -0
  352. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/__init__.py +0 -0
  353. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/cloudflared_login.py +0 -0
  354. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/ensure_cloudflared.py +0 -0
  355. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/ensure_pyyaml.py +0 -0
  356. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/forwarding_state.py +0 -0
  357. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/get_domain.py +0 -0
  358. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/privileged.py +0 -0
  359. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/service_install.py +0 -0
  360. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/tunneling/state.py +0 -0
  361. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/updater.py +0 -0
  362. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
  363. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/__init__.py +0 -0
  364. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/diff_apply.py +0 -0
  365. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/diff_renderer.py +0 -0
  366. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/ntp_clock.py +0 -0
  367. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode/utils/runtime_paths.py +0 -0
  368. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/SOURCES.txt +0 -0
  369. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/dependency_links.txt +0 -0
  370. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/entry_points.txt +0 -0
  371. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/requires.txt +0 -0
  372. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode.egg-info/top_level.txt +0 -0
  373. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/.gitignore +0 -0
  374. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/README.md +0 -0
  375. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/Dockerfile +0 -0
  376. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/README.md +0 -0
  377. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/entrypoint.sh +0 -0
  378. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/persistent_workspace/manage_classroom.py +0 -0
  379. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/Dockerfile +0 -0
  380. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/README.md +0 -0
  381. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docker-compose.yaml +0 -0
  382. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docs/device_transfere.png +0 -0
  383. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/docs/pairing_approval.png +0 -0
  384. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/portacode_for_school/temporary_workspace/entrypoint.sh +0 -0
  385. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/pyproject.toml +0 -0
  386. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/restore.sh +0 -0
  387. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/run_tests.py +0 -0
  388. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/scripts/diagnose_codex_gateway.py +0 -0
  389. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/setup.cfg +0 -0
  390. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/setup.py +0 -0
  391. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test.sh +0 -0
  392. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/README.md +0 -0
  393. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/__init__.py +0 -0
  394. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_device_online.py +0 -0
  395. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_file_operations.py +0 -0
  396. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_git_status_ui.py +0 -0
  397. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_login_flow.py +0 -0
  398. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_navigate_testing_folder.py +0 -0
  399. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_play_store_screenshots.py +0 -0
  400. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_buffer_performance.py +0 -0
  401. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_interaction.py +0 -0
  402. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_loading_race_condition.py +0 -0
  403. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_modules/test_terminal_start.py +0 -0
  404. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_request_id.py +0 -0
  405. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/test_session_terminal_shutdown.py +0 -0
  406. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/.env.example +0 -0
  407. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/README.md +0 -0
  408. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/__init__.py +0 -0
  409. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/cli.py +0 -0
  410. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/__init__.py +0 -0
  411. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/base_test.py +0 -0
  412. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/cli_manager.py +0 -0
  413. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/hierarchical_runner.py +0 -0
  414. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/playwright_manager.py +0 -0
  415. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/runner.py +0 -0
  416. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/shared_cli_manager.py +0 -0
  417. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/core/test_discovery.py +0 -0
  418. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/testing_framework/requirements.txt +0 -0
  419. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
  420. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/UI_UX/server_occasionally_stops_communicating_with_all_devices.md +0 -0
  421. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/agent_context_management.md +0 -0
  422. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/django_server_time_sync.md +0 -0
  423. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/device_performance_degradation.md +0 -0
  424. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
  425. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/indefinite_resource_loading.md +0 -0
  426. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/portacode_service_silently_down.md +0 -0
  427. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/premature_terminal_exit.md +0 -0
  428. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/project_cpu_hotspots.md +0 -0
  429. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/terminals_exit_upon_starting.md +0 -0
  430. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/websocket_client_silently_dead.md +0 -0
  431. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
  432. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/todo/smartphone_terminal_input_frustrations.md +0 -0
  433. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/generate_play_store_assets.py +0 -0
  434. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/pairing_tester.py +0 -0
  435. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/run_screenshot_suite.sh +0 -0
  436. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/tools/test_python_ntp_clock.py +0 -0
  437. {portacode-1.5.14.dev2 → portacode-1.5.14.dev4}/validate.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.14.dev2
3
+ Version: 1.5.14.dev4
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -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.dev2'
22
- __version_tuple__ = version_tuple = (1, 5, 14, 'dev2')
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 terminal sessions are already configured.",
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["OPENAI_API_KEY"] = LOCAL_SENTINEL
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' 'OPENAI_API_KEY=portacode-local' > /etc/portacode/codex.env && "
154
- "chmod 644 /etc/portacode/codex.env && "
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 /etc/portacode/codex.env ]; then' ' set -a' "
157
- "' . /etc/portacode/codex.env' ' set +a' 'fi' > /etc/profile.d/portacode_codex.sh && "
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([*_sudo_prefix(), "sh", "-c", 'grep -q "^OPENAI_API_KEY=portacode-local$" /etc/environment || printf "%s\\n" "OPENAI_API_KEY=portacode-local" >> /etc/environment'])
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 OPENAI_API_KEY=portacode-local\n"
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", "OPENAI_API_KEY", LOCAL_SENTINEL], check=False)
260
+ subprocess.run(["launchctl", "setenv", OPENAI_API_KEY_ENV, LOCAL_SENTINEL], check=False)
169
261
  elif system == "windows":
170
- _run(["setx", "OPENAI_API_KEY", LOCAL_SENTINEL])
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=None, # Inherit the environment where `prepare codex` set OPENAI_API_KEY
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()):
@@ -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
- params: Dict[str, Any] = {"cwd": cwd, "useStateDbOnly": True}
177
- limit = message.get("limit")
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": result.get("threads", []),
188
- "nextCursor": result.get("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
- thread_id = result.get("id") or result.get("threadId")
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": result,
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("thread/read", {"threadId": thread_id})
259
- items = read_result.get("items", read_result.get("messages", []))
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
- try:
76
- for raw_line in CODEX_ENV_PATH.read_text(encoding="utf-8").splitlines():
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 = [f"Environment=SHELL={current_shell}"]
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
- return next(proc_iter)
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
- "thread/start": {"id": "th-1", "name": "New chat"},
47
- "thread/list": {"threads": [{"id": "th-1", "name": "Existing chat"}], "nextCursor": None},
48
- "thread/resume": {},
49
- "thread/read": {"items": [{"id": "msg-1", "role": "user", "text": "hello"}]},
50
- "turn/start": {"id": "turn-1", "threadId": "th-1"},
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.14.dev2
3
+ Version: 1.5.14.dev4
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
File without changes