portacode 1.5.17.dev2__tar.gz → 1.5.17.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 (439) hide show
  1. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/PKG-INFO +1 -1
  2. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/_version.py +2 -2
  3. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/codex_prepare.py +11 -2
  4. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/codex_app_server.py +41 -10
  5. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/git_manager.py +128 -178
  6. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tests/test_codex_app_server.py +14 -6
  7. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/PKG-INFO +1 -1
  8. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/.claude/agents/communication-manager.md +0 -0
  9. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/.claude/settings.local.json +0 -0
  10. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/.gitignore +0 -0
  11. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/.gitmodules +0 -0
  12. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/LICENSE +0 -0
  13. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/MANIFEST.in +0 -0
  14. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/Makefile +0 -0
  15. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/README.md +0 -0
  16. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/backup.sh +0 -0
  17. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/build_android.sh +0 -0
  18. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/connect.py +0 -0
  19. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/connect.sh +0 -0
  20. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docker-compose.yaml +0 -0
  21. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/cloudflared-domain-connect-containers-audit.md +0 -0
  22. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/creative-team-brief-portacode.md +0 -0
  23. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/device-health-alerting-plan.md +0 -0
  24. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/devops-messaging-ab-tests.md +0 -0
  25. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/homepage-dashboard-positioning-fixes.md +0 -0
  26. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/images/device-transfer-button.png +0 -0
  27. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/images/device-transfer-modal.png +0 -0
  28. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/images/pair-device-button.png +0 -0
  29. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/images/pairing-request.png +0 -0
  30. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/images/student-workspace.png +0 -0
  31. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/docs/template-guide.html +0 -0
  32. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/README.md +0 -0
  33. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/simple_device/Dockerfile +0 -0
  34. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/simple_device/README.md +0 -0
  35. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
  36. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  37. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/simple_device/docker-compose.yaml +0 -0
  38. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/Dockerfile +0 -0
  39. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/README.md +0 -0
  40. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
  41. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  42. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
  43. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
  44. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
  45. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
  46. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
  47. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
  48. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
  49. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
  50. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
  51. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
  52. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
  53. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
  54. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
  55. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
  56. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
  57. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
  58. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
  59. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
  60. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
  61. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
  62. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
  63. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
  64. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
  65. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
  66. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
  67. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
  68. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
  69. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
  70. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
  71. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
  72. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
  73. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
  74. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
  75. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
  76. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
  77. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
  78. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
  79. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
  80. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
  81. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
  82. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
  83. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
  84. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
  85. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
  86. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
  87. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
  88. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
  89. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
  90. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
  91. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
  92. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
  93. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
  94. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
  95. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
  96. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
  97. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
  98. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
  99. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
  100. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
  101. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
  102. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
  103. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
  104. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
  105. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
  106. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
  107. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
  108. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
  109. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
  110. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
  111. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
  112. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
  113. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
  114. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
  115. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
  116. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
  117. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
  118. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
  119. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
  120. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
  121. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
  122. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
  123. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
  124. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
  125. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
  126. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
  127. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
  128. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
  129. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
  130. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
  131. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
  132. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
  133. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
  134. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
  135. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
  136. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
  137. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
  138. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
  139. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
  140. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
  141. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
  142. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
  143. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
  144. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
  145. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
  146. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
  147. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
  148. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
  149. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
  150. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
  151. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
  152. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
  153. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
  154. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
  155. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
  156. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
  157. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
  158. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
  159. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
  160. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
  161. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
  162. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
  163. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
  164. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
  165. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
  166. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
  167. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
  168. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
  169. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
  170. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
  171. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
  172. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
  173. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
  174. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
  175. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
  176. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
  177. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
  178. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
  179. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
  180. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
  181. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
  182. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
  183. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
  184. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
  185. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
  186. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
  187. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
  188. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
  189. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
  190. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
  191. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
  192. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
  193. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
  194. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
  195. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
  196. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
  197. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
  198. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
  199. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
  200. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
  201. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
  202. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
  203. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
  204. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
  205. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
  206. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
  207. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
  208. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
  209. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
  210. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
  211. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
  212. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
  213. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
  214. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
  215. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
  216. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
  217. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
  218. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
  219. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
  220. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
  221. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
  222. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
  223. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
  224. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
  225. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
  226. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
  227. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
  228. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
  229. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
  230. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
  231. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
  232. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
  233. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
  234. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
  235. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
  236. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
  237. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
  238. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
  239. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
  240. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
  241. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
  242. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
  243. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
  244. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
  245. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
  246. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
  247. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
  248. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
  249. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
  250. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
  251. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
  252. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
  253. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
  254. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
  255. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/docker-compose.yaml +0 -0
  256. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/README.md +0 -0
  257. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
  258. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
  259. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
  260. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
  261. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
  262. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/manage.py +0 -0
  263. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
  264. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
  265. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
  266. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
  267. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
  268. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
  269. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
  270. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
  271. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
  272. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
  273. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
  274. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
  275. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/README.md +0 -0
  276. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/__init__.py +0 -0
  277. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/__main__.py +0 -0
  278. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/cli.py +0 -0
  279. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/codex_loopback_proxy.py +0 -0
  280. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/codex_usage_limit.py +0 -0
  281. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/README.md +0 -0
  282. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/__init__.py +0 -0
  283. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/client.py +0 -0
  284. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/README.md +0 -0
  285. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
  286. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/__init__.py +0 -0
  287. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/automation_v2_handlers.py +0 -0
  288. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/base.py +0 -0
  289. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/chunked_content.py +0 -0
  290. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/cloudflare_forwarding.py +0 -0
  291. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/cloudflare_tunnel.py +0 -0
  292. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/codex_handlers.py +0 -0
  293. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/diff_handlers.py +0 -0
  294. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/file_handlers.py +0 -0
  295. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/file_transfer_handlers.py +0 -0
  296. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
  297. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/README.md +0 -0
  298. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/__init__.py +0 -0
  299. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
  300. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/handlers.py +0 -0
  301. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/manager.py +0 -0
  302. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/models.py +0 -0
  303. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/test_handlers.py +0 -0
  304. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state/utils.py +0 -0
  305. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/project_state_handlers.py +0 -0
  306. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/proxmox_infra.py +0 -0
  307. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/registry.py +0 -0
  308. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/runtime_user.py +0 -0
  309. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/session.py +0 -0
  310. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/system_handlers.py +0 -0
  311. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/tab_factory.py +0 -0
  312. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/terminal_handlers.py +0 -0
  313. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/test_cloudflare_forwarding.py +0 -0
  314. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/test_proxmox_infra.py +0 -0
  315. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/test_runtime_user.py +0 -0
  316. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/handlers/update_handler.py +0 -0
  317. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/multiplex.py +0 -0
  318. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/terminal.py +0 -0
  319. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/test_webmin_proxy_config.py +0 -0
  320. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/connection/webmin_proxy_config.py +0 -0
  321. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/data.py +0 -0
  322. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/exit_codes.py +0 -0
  323. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/keypair.py +0 -0
  324. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/__init__.py +0 -0
  325. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  326. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/elinks +0 -0
  327. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/gio-open +0 -0
  328. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/gnome-open +0 -0
  329. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/gvfs-open +0 -0
  330. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/kde-open +0 -0
  331. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/kfmclient +0 -0
  332. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
  333. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
  334. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/links +0 -0
  335. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/links2 +0 -0
  336. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/lynx +0 -0
  337. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/mate-open +0 -0
  338. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/netsurf +0 -0
  339. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/sensible-browser +0 -0
  340. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/w3m +0 -0
  341. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/x-www-browser +0 -0
  342. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/link_capture/bin/xdg-open +0 -0
  343. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/logging_categories.py +0 -0
  344. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/pairing.py +0 -0
  345. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/restart.py +0 -0
  346. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/service.py +0 -0
  347. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/static/js/test-ntp-clock.html +0 -0
  348. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/static/js/utils/ntp-clock.js +0 -0
  349. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/test_runtime_paths.py +0 -0
  350. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/test_service.py +0 -0
  351. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/test_terminal_exposed_services.py +0 -0
  352. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tests/test_codex_handlers.py +0 -0
  353. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tests/test_codex_prepare.py +0 -0
  354. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/__init__.py +0 -0
  355. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/cloudflared_login.py +0 -0
  356. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/ensure_cloudflared.py +0 -0
  357. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/ensure_pyyaml.py +0 -0
  358. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/forwarding_state.py +0 -0
  359. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/get_domain.py +0 -0
  360. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/privileged.py +0 -0
  361. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/service_install.py +0 -0
  362. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/tunneling/state.py +0 -0
  363. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/updater.py +0 -0
  364. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
  365. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/__init__.py +0 -0
  366. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/diff_apply.py +0 -0
  367. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/diff_renderer.py +0 -0
  368. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/ntp_clock.py +0 -0
  369. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode/utils/runtime_paths.py +0 -0
  370. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/SOURCES.txt +0 -0
  371. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/dependency_links.txt +0 -0
  372. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/entry_points.txt +0 -0
  373. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/requires.txt +0 -0
  374. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode.egg-info/top_level.txt +0 -0
  375. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/.gitignore +0 -0
  376. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/README.md +0 -0
  377. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/persistent_workspace/Dockerfile +0 -0
  378. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/persistent_workspace/README.md +0 -0
  379. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/persistent_workspace/entrypoint.sh +0 -0
  380. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/persistent_workspace/manage_classroom.py +0 -0
  381. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/Dockerfile +0 -0
  382. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/README.md +0 -0
  383. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/docker-compose.yaml +0 -0
  384. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/docs/device_transfere.png +0 -0
  385. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/docs/pairing_approval.png +0 -0
  386. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/portacode_for_school/temporary_workspace/entrypoint.sh +0 -0
  387. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/pyproject.toml +0 -0
  388. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/restore.sh +0 -0
  389. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/run_tests.py +0 -0
  390. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/scripts/diagnose_codex_gateway.py +0 -0
  391. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/setup.cfg +0 -0
  392. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/setup.py +0 -0
  393. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test.sh +0 -0
  394. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/README.md +0 -0
  395. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/__init__.py +0 -0
  396. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_device_online.py +0 -0
  397. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_file_operations.py +0 -0
  398. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_git_status_ui.py +0 -0
  399. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_login_flow.py +0 -0
  400. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_navigate_testing_folder.py +0 -0
  401. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_play_store_screenshots.py +0 -0
  402. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_terminal_buffer_performance.py +0 -0
  403. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_terminal_interaction.py +0 -0
  404. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_terminal_loading_race_condition.py +0 -0
  405. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_modules/test_terminal_start.py +0 -0
  406. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_request_id.py +0 -0
  407. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/test_session_terminal_shutdown.py +0 -0
  408. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/.env.example +0 -0
  409. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/README.md +0 -0
  410. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/__init__.py +0 -0
  411. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/cli.py +0 -0
  412. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/__init__.py +0 -0
  413. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/base_test.py +0 -0
  414. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/cli_manager.py +0 -0
  415. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/hierarchical_runner.py +0 -0
  416. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/playwright_manager.py +0 -0
  417. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/runner.py +0 -0
  418. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/shared_cli_manager.py +0 -0
  419. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/core/test_discovery.py +0 -0
  420. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/testing_framework/requirements.txt +0 -0
  421. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
  422. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/UI_UX/server_occasionally_stops_communicating_with_all_devices.md +0 -0
  423. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/agent_context_management.md +0 -0
  424. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/django_server_time_sync.md +0 -0
  425. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/device_performance_degradation.md +0 -0
  426. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
  427. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/indefinite_resource_loading.md +0 -0
  428. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/portacode_service_silently_down.md +0 -0
  429. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/premature_terminal_exit.md +0 -0
  430. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/project_cpu_hotspots.md +0 -0
  431. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/terminals_exit_upon_starting.md +0 -0
  432. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/websocket_client_silently_dead.md +0 -0
  433. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
  434. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/todo/smartphone_terminal_input_frustrations.md +0 -0
  435. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/tools/generate_play_store_assets.py +0 -0
  436. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/tools/pairing_tester.py +0 -0
  437. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/tools/run_screenshot_suite.sh +0 -0
  438. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/tools/test_python_ntp_clock.py +0 -0
  439. {portacode-1.5.17.dev2 → portacode-1.5.17.dev4}/validate.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.17.dev2
3
+ Version: 1.5.17.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.17.dev2'
22
- __version_tuple__ = version_tuple = (1, 5, 17, 'dev2')
21
+ __version__ = version = '1.5.17.dev4'
22
+ __version_tuple__ = version_tuple = (1, 5, 17, 'dev4')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -332,6 +332,7 @@ def ensure_codex_home() -> Path:
332
332
 
333
333
  root_codex = Path("/root/.codex")
334
334
  root_sessions = root_codex / "sessions"
335
+ seeded_paths: list[Path] = []
335
336
  if (
336
337
  hasattr(os, "geteuid")
337
338
  and os.geteuid() == 0
@@ -349,6 +350,8 @@ def ensure_codex_home() -> Path:
349
350
  dest.parent.mkdir(parents=True, exist_ok=True)
350
351
  try:
351
352
  shutil.copy2(src, dest)
353
+ seeded_paths.append(dest)
354
+ seeded_paths.append(dest.parent)
352
355
  except OSError:
353
356
  continue
354
357
  for name in ("config.toml", "auth.json"):
@@ -357,10 +360,11 @@ def ensure_codex_home() -> Path:
357
360
  if src.is_file() and not dest.exists():
358
361
  try:
359
362
  shutil.copy2(src, dest)
363
+ seeded_paths.append(dest)
360
364
  except OSError:
361
365
  pass
362
- # Only chown newly created paths full-tree chown on every spawn made
363
- # Codex app-server initialize race the 10s handshake timeout.
366
+ # Chown seeded files (copy2 preserves root ownership). Avoid a full-tree
367
+ # walk on every spawn — that raced the initialize handshake.
364
368
  try:
365
369
  from portacode.connection.handlers.runtime_user import (
366
370
  chown_path_if_possible,
@@ -370,6 +374,11 @@ def ensure_codex_home() -> Path:
370
374
  owner = get_default_runtime_user()
371
375
  chown_path_if_possible(codex_home, owner)
372
376
  chown_path_if_possible(sessions_dir, owner)
377
+ for path in seeded_paths:
378
+ chown_path_if_possible(path, owner)
379
+ for extra in (codex_home / "tmp", codex_home / ".tmp"):
380
+ if extra.exists():
381
+ chown_path_if_possible(extra, owner)
373
382
  except Exception:
374
383
  pass
375
384
  try:
@@ -128,19 +128,27 @@ class CodexAppServer:
128
128
  preserve_env_names=preserve,
129
129
  login=False,
130
130
  )
131
+ # Critical: the agent often runs with cwd=/root (mode 0700). Spawning a
132
+ # non-root Codex via runuser/setpriv while cwd is inaccessible makes
133
+ # Node/Codex hang or exit before answering initialize.
134
+ spawn_cwd = runtime_home if Path(runtime_home).is_dir() else None
131
135
  LOGGER.info(
132
- "Spawning Codex app-server as %s: %s",
136
+ "Spawning Codex app-server as %s (cwd=%s): %s",
133
137
  runtime_user,
138
+ spawn_cwd or ".",
134
139
  " ".join(spawn_cmd),
135
140
  )
141
+ self._recent_stderr: List[str] = []
136
142
  try:
137
- self._proc = await self._process_factory(
138
- *spawn_cmd,
139
- stdin=asyncio.subprocess.PIPE,
140
- stdout=asyncio.subprocess.PIPE,
141
- stderr=asyncio.subprocess.PIPE,
142
- env=child_env,
143
- )
143
+ spawn_kwargs: Dict[str, Any] = {
144
+ "stdin": asyncio.subprocess.PIPE,
145
+ "stdout": asyncio.subprocess.PIPE,
146
+ "stderr": asyncio.subprocess.PIPE,
147
+ "env": child_env,
148
+ }
149
+ if spawn_cwd:
150
+ spawn_kwargs["cwd"] = spawn_cwd
151
+ self._proc = await self._process_factory(*spawn_cmd, **spawn_kwargs)
144
152
  except Exception as exc:
145
153
  raise CodexAppServerError(f"Failed to spawn codex app-server: {exc}") from exc
146
154
 
@@ -241,7 +249,13 @@ class CodexAppServer:
241
249
  return await asyncio.wait_for(future, timeout=timeout)
242
250
  except asyncio.TimeoutError as exc:
243
251
  self._pending.pop(request_id, None)
244
- raise CodexAppServerError(f"codex app-server request timed out: {method}") from exc
252
+ detail = f"codex app-server request timed out: {method}"
253
+ recent = getattr(self, "_recent_stderr", None) or []
254
+ if recent:
255
+ detail = f"{detail} (stderr: {' | '.join(recent[-5:])})"
256
+ if self._proc is not None and self._proc.returncode is not None:
257
+ detail = f"{detail} (process exited code {self._proc.returncode})"
258
+ raise CodexAppServerError(detail) from exc
245
259
 
246
260
  async def _write_notification_on_running_proc(
247
261
  self, method: str, params: Dict[str, Any]
@@ -305,7 +319,16 @@ class CodexAppServer:
305
319
  break
306
320
  text = line.decode("utf-8", errors="replace").rstrip()
307
321
  if text:
308
- LOGGER.debug("codex app-server stderr: %s", text)
322
+ # Keep a small ring for timeout errors; use WARNING so
323
+ # initialize failures are visible at default journal levels.
324
+ buf = getattr(self, "_recent_stderr", None)
325
+ if buf is None:
326
+ self._recent_stderr = []
327
+ buf = self._recent_stderr
328
+ buf.append(text)
329
+ if len(buf) > 40:
330
+ del buf[:-40]
331
+ LOGGER.warning("codex app-server stderr: %s", text)
309
332
  except asyncio.CancelledError:
310
333
  raise
311
334
  except Exception:
@@ -350,6 +373,14 @@ class CodexAppServer:
350
373
  async with self._lock:
351
374
  if self._closed or self._proc is None:
352
375
  return
376
+ # If initialize never completed, the caller still holding start()
377
+ # will surface the error — avoid a restart storm on bad cwd/env.
378
+ if not self._initialized.is_set():
379
+ LOGGER.warning(
380
+ "Codex app-server process exited during startup (code %s)",
381
+ self._proc.returncode,
382
+ )
383
+ return
353
384
  LOGGER.warning("Codex app-server process exited (code %s); restarting", self._proc.returncode)
354
385
  await self._kill_locked()
355
386
  try:
@@ -752,186 +752,132 @@ class GitManager:
752
752
  return None
753
753
 
754
754
 
755
+ def _parse_porcelain_entries(self) -> list[tuple[str, str, str]]:
756
+ """Parse ``git status --porcelain -z`` into (xy, path, orig_path) tuples.
757
+
758
+ Avoids GitPython ``index.diff`` / ``git diff --raw -z``, which can raise
759
+ uncaught IndexError in GitPython's stdout pump thread on some repos.
760
+ """
761
+ try:
762
+ raw = self.repo.git.status(porcelain=True, z=True)
763
+ except Exception as exc:
764
+ logger.debug("git status --porcelain -z failed: %s", exc)
765
+ return []
766
+ if not raw:
767
+ return []
768
+ # git -z separates records with NUL; rename records are "XY\\0path\\0orig\\0"
769
+ # or for porcelain v1 renames: "R new\\0old\\0" depending on version.
770
+ parts = raw.split("\0")
771
+ entries: list[tuple[str, str, str]] = []
772
+ i = 0
773
+ while i < len(parts):
774
+ record = parts[i]
775
+ if not record:
776
+ i += 1
777
+ continue
778
+ if len(record) < 3:
779
+ i += 1
780
+ continue
781
+ xy = record[:2]
782
+ path = record[3:]
783
+ orig = ""
784
+ # Rename/copy may place the other path in the next NUL field.
785
+ if xy[0] in {"R", "C"} and i + 1 < len(parts) and parts[i + 1]:
786
+ # Some git versions emit "XY score\\tnewpath" then old path next.
787
+ if "\t" in path:
788
+ path = path.split("\t", 1)[-1]
789
+ orig = parts[i + 1]
790
+ i += 2
791
+ else:
792
+ i += 1
793
+ if path:
794
+ entries.append((xy, path, orig))
795
+ return entries
796
+
755
797
  def get_detailed_status(self) -> GitDetailedStatus:
756
- """Get detailed Git status with file hashes using GitPython APIs."""
798
+ """Get detailed Git status with file hashes via porcelain (no index.diff)."""
757
799
  if not self.is_git_repo or not self.repo:
758
800
  return GitDetailedStatus()
759
-
801
+
760
802
  try:
761
803
  detailed_status = GitDetailedStatus()
762
804
  detailed_status.head_commit_hash = self.get_head_commit_hash()
763
-
764
- # Get all changed files using GitPython's index diff
765
- # Get staged changes (index vs HEAD)
766
- # Handle case where repository has no commits (no HEAD)
767
- try:
768
- staged_files = self.repo.index.diff("HEAD")
769
- except Exception as e:
770
- logger.debug("🔍 [TRACE] No HEAD found (likely no commits), using staged-only detection: %s", e)
771
- # When no HEAD exists, we need to get staged files differently
772
- staged_file_names = []
773
- try:
774
- staged_output = self.repo.git.diff('--cached', '--name-only')
775
- staged_file_names = staged_output.splitlines() if staged_output.strip() else []
776
- except Exception:
777
- staged_file_names = []
778
- logger.debug("🔍 [TRACE] Found %d staged files in no-HEAD repo: %s", len(staged_file_names), staged_file_names)
779
-
780
- # Create staged file changes manually for no-HEAD repos
781
- for file_repo_path in staged_file_names:
782
- file_abs_path = os.path.join(self.project_path, file_repo_path)
783
- file_name = os.path.basename(file_repo_path)
784
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
785
-
786
- # For staged files in no-HEAD repo, they are all "added" (new files)
787
- diff_details = None
788
-
789
- change = GitFileChange(
790
- file_repo_path=file_repo_path,
791
- file_name=file_name,
792
- file_abs_path=file_abs_path,
793
- change_type='added',
794
- content_hash=content_hash,
795
- is_staged=True,
796
- diff_details=diff_details
797
- )
798
- logger.debug("🔍 [TRACE] Created staged change for no-HEAD repo: %s (added)", file_name)
799
- detailed_status.staged_changes.append(change)
800
-
801
- # Skip the normal staged files loop since we handled it above
802
- staged_files = []
803
- # Get git status --porcelain for accurate change types (GitPython diff can be buggy)
804
- try:
805
- porcelain_status = self.repo.git.status(porcelain=True).strip()
806
- porcelain_map = {}
807
- if porcelain_status:
808
- for line in porcelain_status.split('\n'):
809
- if len(line) >= 3:
810
- index_status = line[0]
811
- file_path = line[3:]
812
- porcelain_map[file_path] = index_status
813
- except Exception:
814
- porcelain_map = {}
815
-
816
- for diff_item in staged_files:
817
- file_repo_path = diff_item.a_path or diff_item.b_path
805
+
806
+ for xy, file_repo_path, _orig in self._parse_porcelain_entries():
807
+ index_status = xy[0] if xy else " "
808
+ work_status = xy[1] if len(xy) > 1 else " "
818
809
  file_abs_path = os.path.join(self.project_path, file_repo_path)
819
810
  file_name = os.path.basename(file_repo_path)
820
-
821
- # Determine change type - use porcelain status for accuracy, fall back to GitPython
822
- porcelain_status = porcelain_map.get(file_repo_path, '')
823
- if porcelain_status == 'A':
824
- change_type = 'untracked'
825
- elif porcelain_status == 'M':
826
- change_type = 'modified'
827
- elif porcelain_status == 'D':
828
- change_type = 'deleted'
829
- else:
830
- # Fall back to GitPython detection
831
- if diff_item.deleted_file:
832
- change_type = 'deleted'
833
- elif diff_item.new_file:
834
- change_type = 'untracked'
835
- else:
836
- change_type = 'modified'
837
-
838
- # Set content hash and diff details based on change type
839
- if change_type == 'deleted':
811
+
812
+ # Untracked
813
+ if index_status == "?" and work_status == "?":
814
+ content_hash = (
815
+ self._compute_file_hash(file_abs_path)
816
+ if os.path.exists(file_abs_path)
817
+ else None
818
+ )
819
+ detailed_status.untracked_files.append(
820
+ GitFileChange(
821
+ file_repo_path=file_repo_path,
822
+ file_name=file_name,
823
+ file_abs_path=file_abs_path,
824
+ change_type="untracked",
825
+ content_hash=content_hash,
826
+ is_staged=False,
827
+ diff_details=None,
828
+ )
829
+ )
830
+ continue
831
+
832
+ # Staged (index column)
833
+ if index_status not in {" ", "?", "!"}:
834
+ # Preserve prior UI mapping: staged "A" was labeled untracked.
835
+ change_type = (
836
+ "untracked"
837
+ if index_status == "A"
838
+ else self._get_change_type(index_status)
839
+ )
840
840
  content_hash = None
841
- diff_details = None # No diff for deleted files
842
- elif change_type == 'untracked':
843
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
844
- # For new files, compare empty content vs current staged content
845
- diff_details = None
846
- else: # modified
847
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
848
- # Compare HEAD content vs staged content
849
- diff_details = None
850
-
851
- change = GitFileChange(
852
- file_repo_path=file_repo_path,
853
- file_name=file_name,
854
- file_abs_path=file_abs_path,
855
- change_type=change_type,
856
- content_hash=content_hash,
857
- is_staged=True,
858
- diff_details=diff_details
859
- )
860
- logger.debug("Created staged change for: %s (%s)", file_name, change_type)
861
- detailed_status.staged_changes.append(change)
862
-
863
- # Get unstaged changes (working tree vs index)
864
- try:
865
- unstaged_files = self.repo.index.diff(None)
866
- except Exception as e:
867
- logger.debug("🔍 [TRACE] Error getting unstaged files: %s", e)
868
- unstaged_files = []
869
- for diff_item in unstaged_files:
870
- file_repo_path = diff_item.a_path or diff_item.b_path
871
- file_abs_path = os.path.join(self.project_path, file_repo_path)
872
- file_name = os.path.basename(file_repo_path)
873
-
874
- # Determine change type - stick to git's native types
875
- if diff_item.deleted_file:
876
- change_type = 'deleted'
841
+ if change_type != "deleted" and os.path.exists(file_abs_path):
842
+ content_hash = self._compute_file_hash(file_abs_path)
843
+ detailed_status.staged_changes.append(
844
+ GitFileChange(
845
+ file_repo_path=file_repo_path,
846
+ file_name=file_name,
847
+ file_abs_path=file_abs_path,
848
+ change_type=change_type,
849
+ content_hash=content_hash,
850
+ is_staged=True,
851
+ diff_details=None,
852
+ )
853
+ )
854
+
855
+ # Unstaged (worktree column)
856
+ if work_status not in {" ", "?", "!"}:
857
+ change_type = self._get_change_type(work_status)
877
858
  content_hash = None
878
- diff_details = None # No diff for deleted files
879
- elif diff_item.new_file:
880
- change_type = 'added'
881
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
882
- # For new files, compare empty content vs current working content
883
- diff_details = None
884
- else:
885
- change_type = 'modified'
886
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
887
- # Compare staged/index content vs working content
888
- diff_details = None
889
-
890
- change = GitFileChange(
891
- file_repo_path=file_repo_path,
892
- file_name=file_name,
893
- file_abs_path=file_abs_path,
894
- change_type=change_type,
895
- content_hash=content_hash,
896
- is_staged=False,
897
- diff_details=diff_details
898
- )
899
- logger.debug("Created unstaged change for: %s (%s)", file_name, change_type)
900
- detailed_status.unstaged_changes.append(change)
901
-
902
- # Get untracked files
903
- try:
904
- untracked_files = self.repo.untracked_files
905
- logger.debug("🔍 [TRACE] Processing %d untracked files: %s", len(untracked_files), untracked_files)
906
- except Exception as e:
907
- logger.debug("🔍 [TRACE] Error getting untracked files: %s", e)
908
- untracked_files = []
909
- for file_repo_path in untracked_files:
910
- file_abs_path = os.path.join(self.project_path, file_repo_path)
911
- file_name = os.path.basename(file_repo_path)
912
- content_hash = self._compute_file_hash(file_abs_path) if os.path.exists(file_abs_path) else None
913
-
914
- # For untracked files, compare empty content vs current file content
915
- diff_details = None
916
-
917
- change = GitFileChange(
918
- file_repo_path=file_repo_path,
919
- file_name=file_name,
920
- file_abs_path=file_abs_path,
921
- change_type='untracked',
922
- content_hash=content_hash,
923
- is_staged=False,
924
- diff_details=diff_details
925
- )
926
- logger.debug("🔍 [TRACE] Created untracked change for: %s", file_name)
927
- detailed_status.untracked_files.append(change)
928
-
929
- logger.debug("🔍 [TRACE] Returning detailed_status with %d staged, %d unstaged, %d untracked",
930
- len(detailed_status.staged_changes),
931
- len(detailed_status.unstaged_changes),
932
- len(detailed_status.untracked_files))
859
+ if change_type != "deleted" and os.path.exists(file_abs_path):
860
+ content_hash = self._compute_file_hash(file_abs_path)
861
+ detailed_status.unstaged_changes.append(
862
+ GitFileChange(
863
+ file_repo_path=file_repo_path,
864
+ file_name=file_name,
865
+ file_abs_path=file_abs_path,
866
+ change_type=change_type,
867
+ content_hash=content_hash,
868
+ is_staged=False,
869
+ diff_details=None,
870
+ )
871
+ )
872
+
873
+ logger.debug(
874
+ "Returning detailed_status with %d staged, %d unstaged, %d untracked",
875
+ len(detailed_status.staged_changes),
876
+ len(detailed_status.unstaged_changes),
877
+ len(detailed_status.untracked_files),
878
+ )
933
879
  return detailed_status
934
-
880
+
935
881
  except Exception as e:
936
882
  logger.error("Error getting detailed Git status: %s", e)
937
883
  return GitDetailedStatus()
@@ -1342,18 +1288,22 @@ class GitManager:
1342
1288
  except Exception:
1343
1289
  has_head = False
1344
1290
 
1345
- if not has_head:
1346
- # For the first commit, check if anything is staged
1291
+ # Prefer porcelain / diff --cached over index.diff (GitPython pump-thread bugs).
1292
+ try:
1293
+ staged_output = self.repo.git.diff("--cached", "--name-only")
1294
+ has_staged = bool(staged_output.strip())
1295
+ except Exception:
1296
+ has_staged = False
1297
+ if not has_staged and not has_head:
1298
+ # First commit with only "git add" of new files still shows in status.
1347
1299
  try:
1348
- staged_output = self.repo.git.diff('--cached', '--name-only')
1349
- has_staged = bool(staged_output.strip())
1300
+ has_staged = any(
1301
+ xy[0] not in {" ", "?", "!"}
1302
+ for xy, _path, _orig in self._parse_porcelain_entries()
1303
+ )
1350
1304
  except Exception:
1351
1305
  has_staged = False
1352
- else:
1353
- # Check if there are staged changes to commit
1354
- staged_changes = self.repo.index.diff("HEAD")
1355
- has_staged = len(staged_changes) > 0
1356
-
1306
+
1357
1307
  if not has_staged:
1358
1308
  raise RuntimeError("No staged changes to commit")
1359
1309
 
@@ -119,19 +119,21 @@ async def test_spawn_injects_openai_api_key(tmp_path, monkeypatch):
119
119
  env_file.write_text("OPENAI_API_KEY=portacode-local\n", encoding="utf-8")
120
120
  monkeypatch.setattr("portacode.codex_prepare.CODEX_ENV_PATH", env_file)
121
121
  monkeypatch.delenv("OPENAI_API_KEY", raising=False)
122
+ home = tmp_path / "home"
123
+ home.mkdir()
122
124
  monkeypatch.setattr(
123
125
  "portacode.connection.handlers.runtime_user.get_default_runtime_user",
124
126
  lambda message=None: "bishoy",
125
127
  )
126
128
  monkeypatch.setattr(
127
129
  "portacode.connection.handlers.runtime_user.get_runtime_user_home",
128
- lambda message=None: str(tmp_path / "home"),
130
+ lambda message=None: str(home),
129
131
  )
130
132
  monkeypatch.setattr(
131
133
  "portacode.connection.handlers.runtime_user.should_switch_user",
132
134
  lambda user: True,
133
135
  )
134
- monkeypatch.setattr("portacode.codex_prepare.ensure_codex_home", lambda: tmp_path / "home" / ".codex")
136
+ monkeypatch.setattr("portacode.codex_prepare.ensure_codex_home", lambda: home / ".codex")
135
137
 
136
138
  proc = FakeProcess([{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "v2"}}])
137
139
  bridge = CodexAppServer(_process_factory=_factory_for(proc))
@@ -139,9 +141,10 @@ async def test_spawn_injects_openai_api_key(tmp_path, monkeypatch):
139
141
 
140
142
  env = getattr(proc, "spawn_kwargs", {}).get("env") or {}
141
143
  assert env.get("OPENAI_API_KEY") == "portacode-local"
142
- assert env.get("HOME") == str(tmp_path / "home")
144
+ assert env.get("HOME") == str(home)
143
145
  assert env.get("USER") == "bishoy"
144
- assert env.get("CODEX_HOME") == str(tmp_path / "home" / ".codex")
146
+ assert env.get("CODEX_HOME") == str(home / ".codex")
147
+ assert getattr(proc, "spawn_kwargs", {}).get("cwd") == str(home)
145
148
  # factory receives argv as *args before stdout/stdin kwargs
146
149
  await bridge.stop()
147
150
 
@@ -150,6 +153,8 @@ async def test_spawn_injects_openai_api_key(tmp_path, monkeypatch):
150
153
  async def test_spawn_runs_as_runtime_user(tmp_path, monkeypatch):
151
154
  env_file = tmp_path / "codex.env"
152
155
  env_file.write_text("OPENAI_API_KEY=portacode-local\n", encoding="utf-8")
156
+ home = tmp_path / "home"
157
+ home.mkdir()
153
158
  monkeypatch.setattr("portacode.codex_prepare.CODEX_ENV_PATH", env_file)
154
159
  monkeypatch.setattr(
155
160
  "portacode.connection.handlers.runtime_user.get_default_runtime_user",
@@ -157,7 +162,7 @@ async def test_spawn_runs_as_runtime_user(tmp_path, monkeypatch):
157
162
  )
158
163
  monkeypatch.setattr(
159
164
  "portacode.connection.handlers.runtime_user.get_runtime_user_home",
160
- lambda message=None: "/home/bishoy",
165
+ lambda message=None: str(home),
161
166
  )
162
167
  monkeypatch.setattr(
163
168
  "portacode.connection.handlers.runtime_user.should_switch_user",
@@ -172,12 +177,14 @@ async def test_spawn_runs_as_runtime_user(tmp_path, monkeypatch):
172
177
 
173
178
  monkeypatch.setattr("portacode.connection.handlers.runtime_user.shutil.which", _which)
174
179
  monkeypatch.setattr("portacode.connection.codex_app_server.shutil.which", _which)
175
- monkeypatch.setattr("portacode.codex_prepare.ensure_codex_home", lambda: tmp_path / ".codex")
180
+ monkeypatch.setattr("portacode.codex_prepare.ensure_codex_home", lambda: home / ".codex")
176
181
 
177
182
  seen: list[tuple] = []
183
+ seen_kwargs: list[dict] = []
178
184
 
179
185
  async def factory(*args, **kwargs):
180
186
  seen.append(args)
187
+ seen_kwargs.append(kwargs)
181
188
  proc = FakeProcess([{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "v2"}}])
182
189
  proc.spawn_kwargs = kwargs
183
190
  return proc
@@ -187,6 +194,7 @@ async def test_spawn_runs_as_runtime_user(tmp_path, monkeypatch):
187
194
  assert seen
188
195
  assert seen[0][:5] == ("/usr/sbin/runuser", "-u", "bishoy", "--preserve-environment", "--")
189
196
  assert "codex" in seen[0]
197
+ assert seen_kwargs[0].get("cwd") == str(home)
190
198
  await bridge.stop()
191
199
 
192
200
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.17.dev2
3
+ Version: 1.5.17.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