portacode 1.5.31.dev1__tar.gz → 1.5.31.dev3__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 (448) hide show
  1. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/PKG-INFO +1 -1
  2. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/_version.py +2 -2
  3. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/cloudflare_forwarding.py +112 -2
  4. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/proxmox_infra.py +53 -0
  5. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/test_proxmox_infra.py +59 -0
  6. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/terminal.py +27 -0
  7. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/PKG-INFO +1 -1
  8. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/.claude/agents/communication-manager.md +0 -0
  9. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/.claude/settings.local.json +0 -0
  10. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/.gitignore +0 -0
  11. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/.gitmodules +0 -0
  12. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/AGENTS.md +0 -0
  13. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/LICENSE +0 -0
  14. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/MANIFEST.in +0 -0
  15. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/Makefile +0 -0
  16. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/README.md +0 -0
  17. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/build_android.sh +0 -0
  18. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/connect.py +0 -0
  19. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/connect.sh +0 -0
  20. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/cloudflared-domain-connect-containers-audit.md +0 -0
  21. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/creative-team-brief-portacode.md +0 -0
  22. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/device-health-alerting-plan.md +0 -0
  23. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/devops-messaging-ab-tests.md +0 -0
  24. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/homepage-dashboard-positioning-fixes.md +0 -0
  25. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/docs/template-guide.html +0 -0
  26. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/README.md +0 -0
  27. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/simple_device/Dockerfile +0 -0
  28. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/simple_device/README.md +0 -0
  29. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
  30. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  31. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/simple_device/docker-compose.yaml +0 -0
  32. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/Dockerfile +0 -0
  33. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/README.md +0 -0
  34. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
  35. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  36. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
  37. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
  38. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
  39. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
  40. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
  41. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
  42. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
  43. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
  44. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
  45. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
  46. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
  47. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
  48. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
  49. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
  50. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
  51. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
  52. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
  53. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
  54. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
  55. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
  56. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
  57. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
  58. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
  59. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
  60. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
  61. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
  62. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
  63. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
  64. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
  65. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
  66. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
  67. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
  68. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
  69. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
  70. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
  71. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
  72. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
  73. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
  74. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
  75. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
  76. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
  77. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
  78. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
  79. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
  80. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
  81. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
  82. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
  83. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
  84. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
  85. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
  86. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
  87. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
  88. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
  89. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
  90. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
  91. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
  92. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
  93. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
  94. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
  95. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
  96. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
  97. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
  98. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
  99. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
  100. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
  101. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
  102. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
  103. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
  104. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
  105. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
  106. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
  107. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
  108. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
  109. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
  110. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
  111. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
  112. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
  113. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
  114. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
  115. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
  116. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
  117. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
  118. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
  119. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
  120. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
  121. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
  122. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
  123. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
  124. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
  125. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
  126. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
  127. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
  128. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
  129. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
  130. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
  131. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
  132. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
  133. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
  134. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
  135. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
  136. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
  137. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
  138. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
  139. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
  140. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
  141. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
  142. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
  143. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
  144. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
  145. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
  146. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
  147. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
  148. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
  149. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
  150. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
  151. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
  152. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
  153. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
  154. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
  155. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
  156. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
  157. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
  158. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
  159. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
  160. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
  161. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
  162. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
  163. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
  164. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
  165. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
  166. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
  167. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
  168. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
  169. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
  170. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
  171. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
  172. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
  173. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
  174. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
  175. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
  176. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
  177. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
  178. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
  179. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
  180. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
  181. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
  182. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
  183. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
  184. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
  185. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
  186. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
  187. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
  188. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
  189. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
  190. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
  191. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
  192. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
  193. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
  194. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
  195. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
  196. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
  197. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
  198. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
  199. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
  200. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
  201. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
  202. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
  203. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
  204. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
  205. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
  206. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
  207. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
  208. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
  209. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
  210. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
  211. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
  212. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
  213. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
  214. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
  215. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
  216. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
  217. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
  218. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
  219. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
  220. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
  221. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
  222. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
  223. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
  224. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
  225. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
  226. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
  227. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
  228. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
  229. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
  230. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
  231. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
  232. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
  233. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
  234. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
  235. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
  236. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
  237. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
  238. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
  239. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
  240. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
  241. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
  242. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
  243. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
  244. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
  245. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
  246. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
  247. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
  248. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
  249. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/docker-compose.yaml +0 -0
  250. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/README.md +0 -0
  251. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
  252. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
  253. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
  254. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
  255. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
  256. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/manage.py +0 -0
  257. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
  258. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
  259. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
  260. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
  261. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
  262. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
  263. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
  264. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
  265. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
  266. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
  267. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
  268. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
  269. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/README.md +0 -0
  270. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/__init__.py +0 -0
  271. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/__main__.py +0 -0
  272. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/cli.py +0 -0
  273. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/codex_loopback_proxy.py +0 -0
  274. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/codex_prepare.py +0 -0
  275. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/codex_usage_limit.py +0 -0
  276. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/README.md +0 -0
  277. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/__init__.py +0 -0
  278. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/client.py +0 -0
  279. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/codex_app_server.py +0 -0
  280. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/README.md +0 -0
  281. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +0 -0
  282. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/__init__.py +0 -0
  283. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/automation_v2_handlers.py +0 -0
  284. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/base.py +0 -0
  285. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/browser_handlers.py +0 -0
  286. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/chunked_content.py +0 -0
  287. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/cloudflare_tunnel.py +0 -0
  288. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/codex_handlers.py +0 -0
  289. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/diff_handlers.py +0 -0
  290. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/file_handlers.py +0 -0
  291. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/file_transfer_handlers.py +0 -0
  292. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
  293. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/README.md +0 -0
  294. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/__init__.py +0 -0
  295. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
  296. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/git_manager.py +0 -0
  297. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/handlers.py +0 -0
  298. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/manager.py +0 -0
  299. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/models.py +0 -0
  300. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/test_handlers.py +0 -0
  301. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state/utils.py +0 -0
  302. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/project_state_handlers.py +0 -0
  303. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/registry.py +0 -0
  304. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/resumable_transfer_handlers.py +0 -0
  305. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/runtime_user.py +0 -0
  306. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/session.py +0 -0
  307. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/system_handlers.py +0 -0
  308. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/tab_factory.py +0 -0
  309. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/terminal_handlers.py +0 -0
  310. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/test_browser_handlers.py +0 -0
  311. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/test_cloudflare_forwarding.py +0 -0
  312. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/test_file_move_copy.py +0 -0
  313. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/test_runtime_user.py +0 -0
  314. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/handlers/update_handler.py +0 -0
  315. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/multiplex.py +0 -0
  316. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/test_webmin_proxy_config.py +0 -0
  317. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/connection/webmin_proxy_config.py +0 -0
  318. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/data.py +0 -0
  319. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/exit_codes.py +0 -0
  320. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/github_credential.py +0 -0
  321. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/keypair.py +0 -0
  322. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/__init__.py +0 -0
  323. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  324. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/__pycache__/__init__.cpython-38.pyc +0 -0
  325. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
  326. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-38.pyc +0 -0
  327. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/elinks +0 -0
  328. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/gio-open +0 -0
  329. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/gnome-open +0 -0
  330. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/gvfs-open +0 -0
  331. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/kde-open +0 -0
  332. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/kfmclient +0 -0
  333. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
  334. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
  335. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/links +0 -0
  336. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/links2 +0 -0
  337. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/lynx +0 -0
  338. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/mate-open +0 -0
  339. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/netsurf +0 -0
  340. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/sensible-browser +0 -0
  341. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/w3m +0 -0
  342. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/x-www-browser +0 -0
  343. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/link_capture/bin/xdg-open +0 -0
  344. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/logging_categories.py +0 -0
  345. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/pairing.py +0 -0
  346. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/provisioning_cache.py +0 -0
  347. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/restart.py +0 -0
  348. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/service.py +0 -0
  349. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/singleton.py +0 -0
  350. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/static/js/test-ntp-clock.html +0 -0
  351. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/static/js/utils/ntp-clock.js +0 -0
  352. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/test_runtime_paths.py +0 -0
  353. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/test_service.py +0 -0
  354. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/test_singleton.py +0 -0
  355. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/test_terminal_exposed_services.py +0 -0
  356. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_codex_app_server.py +0 -0
  357. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_codex_handlers.py +0 -0
  358. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_codex_loopback_proxy.py +0 -0
  359. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_codex_prepare.py +0 -0
  360. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_github_credential.py +0 -0
  361. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_image_read_handler.py +0 -0
  362. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_resumable_transfer_handlers.py +0 -0
  363. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tests/test_system_handlers.py +0 -0
  364. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/__init__.py +0 -0
  365. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/cloudflared_login.py +0 -0
  366. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/ensure_cloudflared.py +0 -0
  367. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/ensure_pyyaml.py +0 -0
  368. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/forwarding_state.py +0 -0
  369. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/get_domain.py +0 -0
  370. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/privileged.py +0 -0
  371. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/service_install.py +0 -0
  372. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/tunneling/state.py +0 -0
  373. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/updater.py +0 -0
  374. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
  375. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/__init__.py +0 -0
  376. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/diff_apply.py +0 -0
  377. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/diff_renderer.py +0 -0
  378. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/ntp_clock.py +0 -0
  379. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode/utils/runtime_paths.py +0 -0
  380. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/SOURCES.txt +0 -0
  381. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/dependency_links.txt +0 -0
  382. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/entry_points.txt +0 -0
  383. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/requires.txt +0 -0
  384. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode.egg-info/top_level.txt +0 -0
  385. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/.gitignore +0 -0
  386. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/README.md +0 -0
  387. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/persistent_workspace/Dockerfile +0 -0
  388. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/persistent_workspace/README.md +0 -0
  389. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/persistent_workspace/entrypoint.sh +0 -0
  390. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/persistent_workspace/manage_classroom.py +0 -0
  391. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/Dockerfile +0 -0
  392. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/README.md +0 -0
  393. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/docker-compose.yaml +0 -0
  394. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/docs/device_transfere.png +0 -0
  395. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/docs/pairing_approval.png +0 -0
  396. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/portacode_for_school/temporary_workspace/entrypoint.sh +0 -0
  397. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/pyproject.toml +0 -0
  398. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/run_tests.py +0 -0
  399. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/scripts/diagnose_codex_gateway.py +0 -0
  400. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/setup.cfg +0 -0
  401. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/setup.py +0 -0
  402. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test.sh +0 -0
  403. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/README.md +0 -0
  404. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/__init__.py +0 -0
  405. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_device_online.py +0 -0
  406. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_file_operations.py +0 -0
  407. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_git_status_ui.py +0 -0
  408. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_login_flow.py +0 -0
  409. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_navigate_testing_folder.py +0 -0
  410. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_play_store_screenshots.py +0 -0
  411. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_terminal_buffer_performance.py +0 -0
  412. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_terminal_interaction.py +0 -0
  413. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_terminal_loading_race_condition.py +0 -0
  414. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_modules/test_terminal_start.py +0 -0
  415. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_request_id.py +0 -0
  416. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/test_session_terminal_shutdown.py +0 -0
  417. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/.env.example +0 -0
  418. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/README.md +0 -0
  419. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/__init__.py +0 -0
  420. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/cli.py +0 -0
  421. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/__init__.py +0 -0
  422. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/base_test.py +0 -0
  423. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/cli_manager.py +0 -0
  424. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/hierarchical_runner.py +0 -0
  425. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/playwright_manager.py +0 -0
  426. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/runner.py +0 -0
  427. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/shared_cli_manager.py +0 -0
  428. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/core/test_discovery.py +0 -0
  429. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/testing_framework/requirements.txt +0 -0
  430. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
  431. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/UI_UX/server_occasionally_stops_communicating_with_all_devices.md +0 -0
  432. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/agent_context_management.md +0 -0
  433. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/django_server_time_sync.md +0 -0
  434. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/device_performance_degradation.md +0 -0
  435. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
  436. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/indefinite_resource_loading.md +0 -0
  437. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/portacode_service_silently_down.md +0 -0
  438. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/premature_terminal_exit.md +0 -0
  439. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/project_cpu_hotspots.md +0 -0
  440. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/terminals_exit_upon_starting.md +0 -0
  441. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/websocket_client_silently_dead.md +0 -0
  442. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
  443. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/todo/smartphone_terminal_input_frustrations.md +0 -0
  444. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/tools/generate_play_store_assets.py +0 -0
  445. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/tools/pairing_tester.py +0 -0
  446. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/tools/run_screenshot_suite.sh +0 -0
  447. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/tools/test_python_ntp_clock.py +0 -0
  448. {portacode-1.5.31.dev1 → portacode-1.5.31.dev3}/validate.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.31.dev1
3
+ Version: 1.5.31.dev3
4
4
  Summary: Connect real machines to the Portacode AI development environment
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.31.dev1'
22
- __version_tuple__ = version_tuple = (1, 5, 31, 'dev1')
21
+ __version__ = version = '1.5.31.dev3'
22
+ __version_tuple__ = version_tuple = (1, 5, 31, 'dev3')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -10,6 +10,7 @@ import re
10
10
  import subprocess
11
11
  import threading
12
12
  import tempfile
13
+ import time
13
14
  from pathlib import Path
14
15
  from typing import Any, Dict, List, Optional
15
16
  from urllib.parse import urlparse
@@ -58,6 +59,16 @@ GLOBAL_SHELL_HOOK_PATHS = (
58
59
  MANAGED_BLOCK_BEGIN = "# >>> PORTACODE_EXPOSED_SERVICES >>>"
59
60
  MANAGED_BLOCK_END = "# <<< PORTACODE_EXPOSED_SERVICES <<<"
60
61
 
62
+
63
+ def _timed_stage(operation_id: str, stage: str, started: float, **details: Any) -> None:
64
+ logger.info(
65
+ "Expose ports timing operation_id=%s stage=%s elapsed_s=%.3f details=%s",
66
+ operation_id,
67
+ stage,
68
+ time.monotonic() - started,
69
+ details,
70
+ )
71
+
61
72
  def _is_root() -> bool:
62
73
  return hasattr(os, "geteuid") and os.geteuid() == 0
63
74
 
@@ -301,11 +312,17 @@ def _route_dns(hostnames: List[str], tunnel_name: str) -> None:
301
312
  if hostname in seen:
302
313
  continue
303
314
  seen.add(hostname)
315
+ started = time.monotonic()
304
316
  try:
305
317
  _call_subprocess(
306
318
  ["cloudflared", "tunnel", "route", "dns", "--overwrite-dns", tunnel_name, hostname],
307
319
  check=True,
308
320
  )
321
+ logger.info(
322
+ "Expose ports timing stage=route_dns hostname=%s elapsed_s=%.3f",
323
+ hostname,
324
+ time.monotonic() - started,
325
+ )
309
326
  except subprocess.CalledProcessError as exc:
310
327
  stderr = (getattr(exc, "stderr", "") or "").strip()
311
328
  stdout = (getattr(exc, "stdout", "") or "").strip()
@@ -318,7 +335,14 @@ def _route_dns(hostnames: List[str], tunnel_name: str) -> None:
318
335
 
319
336
  def _reload_cloudflared_service() -> None:
320
337
  # Restart via init-system aware logic; sudo/root handled inside.
321
- restart_service()
338
+ started = time.monotonic()
339
+ try:
340
+ restart_service()
341
+ finally:
342
+ logger.info(
343
+ "Expose ports timing stage=reload_cloudflared elapsed_s=%.3f",
344
+ time.monotonic() - started,
345
+ )
322
346
 
323
347
 
324
348
  def _load_tunnel_state() -> Dict[str, Any]:
@@ -349,20 +373,34 @@ def _apply_and_persist_forwarding_rules(
349
373
  if not domain or not tunnel_name:
350
374
  raise RuntimeError("Cloudflare domain or tunnel name missing from state.")
351
375
 
376
+ operation_id = f"apply-{time.monotonic_ns()}"
377
+ total_started = time.monotonic()
352
378
  requires_proxmox = any(rule["parsed"]["type"] == "device" for rule in rules)
353
379
  proxmox = node = None
354
380
  if requires_proxmox:
355
381
  infra_config = _ensure_infra_configured()
356
382
  proxmox = _connect_proxmox(infra_config)
357
383
  node = _get_node_from_config(infra_config)
384
+ _timed_stage(operation_id, "connect_proxmox", total_started, required=requires_proxmox)
358
385
 
386
+ started = time.monotonic()
359
387
  entries = _build_ingress_entries(rules, proxmox, node)
388
+ _timed_stage(operation_id, "build_ingress_entries", started, rule_count=len(rules))
389
+ started = time.monotonic()
360
390
  _write_cloudflared_config(tunnel_state, entries)
391
+ _timed_stage(operation_id, "write_cloudflared_config", started, entry_count=len(entries))
361
392
  hostnames = [entry["hostname"] for entry in entries if entry.get("hostname")]
362
393
  if hostnames:
394
+ started = time.monotonic()
363
395
  _route_dns(hostnames, tunnel_name)
396
+ _timed_stage(operation_id, "route_dns_all", started, hostname_count=len(hostnames))
397
+ started = time.monotonic()
364
398
  _reload_cloudflared_service()
399
+ _timed_stage(operation_id, "reload_cloudflared_call", started)
400
+ started = time.monotonic()
365
401
  state = persist_forwarding_state(rules)
402
+ _timed_stage(operation_id, "persist_forwarding_state", started, rule_count=len(rules))
403
+ _timed_stage(operation_id, "apply_total", total_started, rule_count=len(rules))
366
404
  return {
367
405
  "rules": _sanitize_rules(rules),
368
406
  "updated_at": state.get("updated_at"),
@@ -414,18 +452,48 @@ def _push_root_file_to_container(vmid: int, path: str, data: bytes, mode: int =
414
452
  try:
415
453
  parent = str(Path(path).parent)
416
454
  if parent not in {"", ".", "/"}:
455
+ started = time.monotonic()
417
456
  _run_pct_exec_check(vmid, ["mkdir", "-p", parent])
457
+ logger.info(
458
+ "Expose ports timing stage=pct_mkdir vmid=%s elapsed_s=%.3f",
459
+ vmid,
460
+ time.monotonic() - started,
461
+ )
462
+ started = time.monotonic()
418
463
  _run_pct_exec_check(vmid, ["chown", "root:root", parent])
464
+ logger.info(
465
+ "Expose ports timing stage=pct_chown_parent vmid=%s elapsed_s=%.3f",
466
+ vmid,
467
+ time.monotonic() - started,
468
+ )
419
469
  with tempfile.NamedTemporaryFile(delete=False) as tmp:
420
470
  tmp.write(data)
421
471
  tmp.flush()
422
472
  os.fsync(tmp.fileno())
423
473
  tmp_path = tmp.name
474
+ started = time.monotonic()
424
475
  res = _run_pct_push(vmid, tmp_path, path)
476
+ logger.info(
477
+ "Expose ports timing stage=pct_push vmid=%s elapsed_s=%.3f",
478
+ vmid,
479
+ time.monotonic() - started,
480
+ )
425
481
  if res.returncode != 0:
426
482
  raise RuntimeError(res.stderr or res.stdout or f"pct push returned {res.returncode}")
483
+ started = time.monotonic()
427
484
  _run_pct_exec_check(vmid, ["chown", "root:root", path])
485
+ logger.info(
486
+ "Expose ports timing stage=pct_chown_file vmid=%s elapsed_s=%.3f",
487
+ vmid,
488
+ time.monotonic() - started,
489
+ )
490
+ started = time.monotonic()
428
491
  _run_pct_exec_check(vmid, ["chmod", format(mode, "o"), path])
492
+ logger.info(
493
+ "Expose ports timing stage=pct_chmod_file vmid=%s elapsed_s=%.3f",
494
+ vmid,
495
+ time.monotonic() - started,
496
+ )
429
497
  finally:
430
498
  if tmp_path:
431
499
  try:
@@ -607,13 +675,29 @@ def _sync_exposed_services_into_container(
607
675
  proxmox: Any,
608
676
  node: str,
609
677
  ) -> None:
678
+ started = time.monotonic()
610
679
  vmid = _resolve_device_vmid(container_device_id, proxmox, node)
680
+ logger.info(
681
+ "Expose ports timing stage=resolve_target_vmid device_id=%s vmid=%s elapsed_s=%.3f",
682
+ container_device_id,
683
+ vmid,
684
+ time.monotonic() - started,
685
+ )
611
686
  services_payload = {
612
687
  "device_id": container_device_id,
613
688
  "exposed_services": exposed_ports,
614
689
  }
615
690
  json_data = (json.dumps(services_payload, indent=2) + "\n").encode("utf-8")
616
- _push_root_file_to_container(vmid, EXPOSED_SERVICES_JSON_PATH, json_data, mode=0o644)
691
+ started = time.monotonic()
692
+ try:
693
+ _push_root_file_to_container(vmid, EXPOSED_SERVICES_JSON_PATH, json_data, mode=0o644)
694
+ finally:
695
+ logger.info(
696
+ "Expose ports timing stage=sync_container_metadata device_id=%s vmid=%s elapsed_s=%.3f",
697
+ container_device_id,
698
+ vmid,
699
+ time.monotonic() - started,
700
+ )
617
701
 
618
702
 
619
703
  def _rule_targets_container(rule: Dict[str, Any], container_device_id: str, domain: str) -> bool:
@@ -639,12 +723,19 @@ def set_container_forwarding_rules(
639
723
  container_device_id: Any,
640
724
  container_rules: List[Dict[str, Any]],
641
725
  ) -> Dict[str, Any]:
726
+ operation_started = time.monotonic()
642
727
  tunnel_state = _load_tunnel_state()
643
728
  domain = str(tunnel_state.get("domain") or "").strip().lower().rstrip(".")
644
729
  normalized_specs = _normalize_container_rule_specs(container_device_id, container_rules)
645
730
  normalized_device_id = str(container_device_id).strip()
646
731
 
647
732
  with _FORWARDING_UPDATE_LOCK:
733
+ lock_acquired = time.monotonic()
734
+ logger.info(
735
+ "Expose ports timing stage=lock_wait device_id=%s elapsed_s=%.3f",
736
+ normalized_device_id,
737
+ lock_acquired - operation_started,
738
+ )
648
739
  stored = load_forwarding_state().get("rules", [])
649
740
  existing_rules = _normalize_rules(stored, domain, from_storage=True)
650
741
  preserved_rules = [
@@ -676,16 +767,28 @@ def set_container_forwarding_rules(
676
767
  merged_rules = preserved_rules + new_rules
677
768
 
678
769
  # Ensure the child container receives updated exposure metadata first.
770
+ started = time.monotonic()
679
771
  infra_config = _ensure_infra_configured()
680
772
  proxmox = _connect_proxmox(infra_config)
681
773
  node = _get_node_from_config(infra_config)
774
+ logger.info(
775
+ "Expose ports timing stage=metadata_connect_proxmox device_id=%s elapsed_s=%.3f",
776
+ normalized_device_id,
777
+ time.monotonic() - started,
778
+ )
682
779
  try:
780
+ started = time.monotonic()
683
781
  _sync_exposed_services_into_container(
684
782
  normalized_device_id,
685
783
  exposed_ports,
686
784
  proxmox,
687
785
  node,
688
786
  )
787
+ logger.info(
788
+ "Expose ports timing stage=metadata_total device_id=%s elapsed_s=%.3f",
789
+ normalized_device_id,
790
+ time.monotonic() - started,
791
+ )
689
792
  except Exception:
690
793
  if exposed_ports:
691
794
  raise
@@ -697,6 +800,13 @@ def set_container_forwarding_rules(
697
800
 
698
801
  persisted = _apply_and_persist_forwarding_rules(merged_rules, tunnel_state=tunnel_state)
699
802
 
803
+ logger.info(
804
+ "Expose ports timing stage=request_total device_id=%s elapsed_s=%.3f rule_count=%s",
805
+ normalized_device_id,
806
+ time.monotonic() - operation_started,
807
+ len(merged_rules),
808
+ )
809
+
700
810
  return {
701
811
  "device_id": normalized_device_id,
702
812
  "domain": domain,
@@ -53,6 +53,7 @@ CACHE_SOURCE_MARKER = "cache_source="
53
53
  CACHE_PARENT_MARKER = "cache_parent="
54
54
  _PROVISIONING_MAINTENANCE_LAST_RUN = 0.0
55
55
  PROVISIONING_MAINTENANCE_INTERVAL_S = 300
56
+ PROXMOX_INVENTORY_RECONCILE_INTERVAL_S = 300
56
57
  # Keep reusable roots small while leaving enough room for the OS, Portacode,
57
58
  # Codex, and (on apt systems) Playwright Chromium to be installed. A request
58
59
  # below this size still builds at its requested size; if the software cannot
@@ -154,6 +155,7 @@ _MANAGED_CONTAINERS_STATE: Dict[str, Any] = {
154
155
  "initial_totals": {"ram_mib": 0, "disk_gib": 0, "cpu_share": 0.0},
155
156
  "records": {},
156
157
  "pending": {},
158
+ "revision": 0,
157
159
  }
158
160
  TEMPLATES_REFRESH_INTERVAL_S = 300
159
161
  _TEMPLATE_CACHE_LOCK = threading.Lock()
@@ -1340,9 +1342,47 @@ def _initialize_managed_containers_state(force: bool = False) -> Dict[str, Any]:
1340
1342
  _MANAGED_CONTAINERS_STATE["initial_totals"] = initial_totals
1341
1343
  _MANAGED_CONTAINERS_STATE["records"] = record_map
1342
1344
  _MANAGED_CONTAINERS_STATE["pending"] = {}
1345
+ _MANAGED_CONTAINERS_STATE["revision"] = int(
1346
+ _MANAGED_CONTAINERS_STATE.get("revision") or 0
1347
+ ) + 1
1343
1348
  return _copy_summary(base_summary)
1344
1349
 
1345
1350
 
1351
+ def reconcile_managed_containers_inventory() -> Dict[str, Any]:
1352
+ """Refresh Proxmox inventory without blocking or racing provisioning.
1353
+
1354
+ The comparatively expensive Proxmox API scan runs outside the state lock.
1355
+ A revision check makes the final swap conditional: if a managed create,
1356
+ delete, or record update occurred during the scan, the stale result is
1357
+ discarded and the next periodic pass retries it. Pending reservations are
1358
+ never replaced by reconciliation.
1359
+ """
1360
+ _initialize_managed_containers_state()
1361
+ with _MANAGED_CONTAINERS_STATE_LOCK:
1362
+ revision = int(_MANAGED_CONTAINERS_STATE.get("revision") or 0)
1363
+ records = [record.copy() for record in _MANAGED_CONTAINERS_STATE.get("records", {}).values()]
1364
+
1365
+ config = _load_config()
1366
+ if not config:
1367
+ return {"refreshed": False, "reason": "not_configured"}
1368
+ refreshed_summary = _build_full_container_summary(records, config)
1369
+ refreshed_totals = {
1370
+ "ram_mib": int(refreshed_summary.get("total_ram_mib") or 0),
1371
+ "disk_gib": int(refreshed_summary.get("total_disk_gib") or 0),
1372
+ "cpu_share": float(refreshed_summary.get("total_cpu_share") or 0.0),
1373
+ }
1374
+
1375
+ with _MANAGED_CONTAINERS_STATE_LOCK:
1376
+ if int(_MANAGED_CONTAINERS_STATE.get("revision") or 0) != revision:
1377
+ return {"refreshed": False, "reason": "state_changed"}
1378
+ _MANAGED_CONTAINERS_STATE["base_summary"] = refreshed_summary
1379
+ _MANAGED_CONTAINERS_STATE["initial_totals"] = refreshed_totals
1380
+ return {
1381
+ "refreshed": True,
1382
+ "inventory_updated_at": refreshed_summary.get("inventory_updated_at"),
1383
+ }
1384
+
1385
+
1346
1386
  def _load_managed_container_records() -> List[Dict[str, Any]]:
1347
1387
  _ensure_containers_dir()
1348
1388
  records: List[Dict[str, Any]] = []
@@ -1668,10 +1708,17 @@ def _compose_managed_containers_summary(
1668
1708
  for entry in base_summary.get("containers", [])
1669
1709
  if entry.get("vmid") is not None
1670
1710
  }
1711
+ missing_vmids = {
1712
+ str(entry.get("vmid"))
1713
+ for entry in base_summary.get("missing_containers", [])
1714
+ if entry.get("vmid") is not None
1715
+ }
1671
1716
  default_storage = summary.get("default_storage")
1672
1717
  merged_containers: List[Dict[str, Any]] = []
1673
1718
  for entry in managed_summary["containers"]:
1674
1719
  vmid = str(entry.get("vmid")) if entry.get("vmid") is not None else None
1720
+ if vmid in missing_vmids:
1721
+ continue
1675
1722
  base_entry = base_by_vmid.get(vmid) if vmid is not None else None
1676
1723
  if base_entry:
1677
1724
  merged = base_entry.copy()
@@ -2832,12 +2879,18 @@ def _register_container_record(vmid: int, payload: Dict[str, Any], reservation_i
2832
2879
  if reservation_id:
2833
2880
  _MANAGED_CONTAINERS_STATE["pending"].pop(reservation_id, None)
2834
2881
  _MANAGED_CONTAINERS_STATE["records"][str(vmid)] = payload.copy()
2882
+ _MANAGED_CONTAINERS_STATE["revision"] = int(
2883
+ _MANAGED_CONTAINERS_STATE.get("revision") or 0
2884
+ ) + 1
2835
2885
 
2836
2886
 
2837
2887
  def _unregister_container_record(vmid: int) -> None:
2838
2888
  _initialize_managed_containers_state()
2839
2889
  with _MANAGED_CONTAINERS_STATE_LOCK:
2840
2890
  _MANAGED_CONTAINERS_STATE["records"].pop(str(vmid), None)
2891
+ _MANAGED_CONTAINERS_STATE["revision"] = int(
2892
+ _MANAGED_CONTAINERS_STATE.get("revision") or 0
2893
+ ) + 1
2841
2894
 
2842
2895
 
2843
2896
  def _write_container_record(vmid: int, payload: Dict[str, Any], reservation_id: Optional[str] = None) -> None:
@@ -6,6 +6,7 @@ from unittest.mock import MagicMock, patch
6
6
  from portacode.connection.handlers.proxmox_infra import (
7
7
  RemoveProxmoxContainerHandler,
8
8
  _build_full_container_summary,
9
+ _compose_managed_containers_summary,
9
10
  _build_bootstrap_steps,
10
11
  _cacheable_bootstrap_steps,
11
12
  _claim_ctid_for_reservation,
@@ -26,11 +27,69 @@ from portacode.connection.handlers.proxmox_infra import (
26
27
  _running_portacode_version_is_prerelease,
27
28
  _require_successful_proxmox_task,
28
29
  _reserve_container_resources,
30
+ reconcile_managed_containers_inventory,
29
31
  _sanitize_project_paths,
30
32
  )
31
33
 
32
34
 
33
35
  class ProxmoxInfraHandlerTests(TestCase):
36
+ def test_reconciled_external_deletion_is_not_reinserted_from_local_record(self):
37
+ summary = _compose_managed_containers_summary(
38
+ [{"vmid": 144, "device_id": "42", "ram_mib": 2048, "disk_gib": 14, "cpus": 1}],
39
+ [],
40
+ {
41
+ "containers": [],
42
+ "missing_containers": [{"vmid": "144", "device_id": "42"}],
43
+ "allocated_ram_mib": 0,
44
+ "allocated_disk_gib": 0,
45
+ "allocated_cpu_share": 0,
46
+ },
47
+ {"ram_mib": 2048, "disk_gib": 14, "cpu_share": 1},
48
+ )
49
+
50
+ self.assertEqual(summary["containers"], [])
51
+ self.assertEqual(summary["missing_containers"][0]["vmid"], "144")
52
+
53
+ @patch("portacode.connection.handlers.proxmox_infra._build_full_container_summary")
54
+ @patch("portacode.connection.handlers.proxmox_infra._load_config", return_value={"node": "pve"})
55
+ def test_reconciliation_atomically_replaces_inventory_baseline(self, _config, build_summary):
56
+ refreshed = {"inventory_updated_at": "new", "total_ram_mib": 2, "total_disk_gib": 3, "total_cpu_share": 1}
57
+ build_summary.return_value = refreshed
58
+ state = {
59
+ "initialized": True, "base_summary": {"inventory_updated_at": "old"},
60
+ "initial_totals": {}, "records": {"144": {"vmid": 144}},
61
+ "pending": {"reservation": {"ram_mib": 1}}, "revision": 4,
62
+ }
63
+ with patch("portacode.connection.handlers.proxmox_infra._MANAGED_CONTAINERS_STATE", state):
64
+ result = reconcile_managed_containers_inventory()
65
+
66
+ self.assertTrue(result["refreshed"])
67
+ self.assertIs(state["base_summary"], refreshed)
68
+ self.assertIn("reservation", state["pending"])
69
+ self.assertEqual(state["revision"], 4)
70
+
71
+ @patch("portacode.connection.handlers.proxmox_infra._load_config", return_value={"node": "pve"})
72
+ def test_reconciliation_discards_scan_when_managed_state_changes(self, _config):
73
+ state = {
74
+ "initialized": True, "base_summary": {"inventory_updated_at": "old"},
75
+ "initial_totals": {}, "records": {"144": {"vmid": 144}},
76
+ "pending": {}, "revision": 7,
77
+ }
78
+
79
+ def concurrent_scan(_records, _config):
80
+ state["records"]["145"] = {"vmid": 145}
81
+ state["revision"] += 1
82
+ return {"inventory_updated_at": "stale scan"}
83
+
84
+ with patch("portacode.connection.handlers.proxmox_infra._MANAGED_CONTAINERS_STATE", state), patch(
85
+ "portacode.connection.handlers.proxmox_infra._build_full_container_summary",
86
+ side_effect=concurrent_scan,
87
+ ):
88
+ result = reconcile_managed_containers_inventory()
89
+
90
+ self.assertEqual(result, {"refreshed": False, "reason": "state_changed"})
91
+ self.assertEqual(state["base_summary"]["inventory_updated_at"], "old")
92
+
34
93
  def test_cpu_can_be_oversubscribed_when_ram_and_disk_are_available(self):
35
94
  state = {
36
95
  "initialized": True,
@@ -513,6 +513,15 @@ class TerminalManager:
513
513
  self._periodic_provisioning_template_maintenance()
514
514
  )
515
515
 
516
+ if getattr(self, "_proxmox_inventory_reconciliation_task", None):
517
+ try:
518
+ self._proxmox_inventory_reconciliation_task.cancel()
519
+ except Exception:
520
+ pass
521
+ self._proxmox_inventory_reconciliation_task = asyncio.create_task(
522
+ self._periodic_proxmox_inventory_reconciliation()
523
+ )
524
+
516
525
  # Start exposed-services file monitor for realtime updates.
517
526
  if getattr(self, "_exposed_services_task", None):
518
527
  try:
@@ -686,6 +695,24 @@ class TerminalManager:
686
695
  logger.exception("Provisioning template maintenance failed: %s", exc)
687
696
  await asyncio.sleep(PROVISIONING_MAINTENANCE_INTERVAL_S)
688
697
 
698
+ async def _periodic_proxmox_inventory_reconciliation(self) -> None:
699
+ """Periodically detect changes made directly through Proxmox."""
700
+ from .handlers.proxmox_infra import (
701
+ PROXMOX_INVENTORY_RECONCILE_INTERVAL_S,
702
+ reconcile_managed_containers_inventory,
703
+ )
704
+
705
+ while True:
706
+ await asyncio.sleep(PROXMOX_INVENTORY_RECONCILE_INTERVAL_S)
707
+ try:
708
+ result = await asyncio.to_thread(reconcile_managed_containers_inventory)
709
+ if result.get("reason") == "state_changed":
710
+ logger.debug("Discarded Proxmox inventory scan after concurrent state change")
711
+ except asyncio.CancelledError:
712
+ raise
713
+ except Exception as exc:
714
+ logger.warning("Proxmox inventory reconciliation failed safely: %s", exc)
715
+
689
716
  def _read_exposed_services_snapshot(self) -> Dict[str, Any]:
690
717
  path = EXPOSED_SERVICES_MONITOR_PATH
691
718
  if not os.path.exists(path):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.5.31.dev1
3
+ Version: 1.5.31.dev3
4
4
  Summary: Connect real machines to the Portacode AI development environment
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
File without changes