portacode 1.4.18.dev0__tar.gz → 1.4.19__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 (383) hide show
  1. {portacode-1.4.18.dev0 → portacode-1.4.19}/PKG-INFO +1 -1
  2. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/_version.py +2 -2
  3. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +4 -3
  4. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/base.py +26 -5
  5. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/proxmox_infra.py +193 -17
  6. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/registry.py +16 -3
  7. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/PKG-INFO +1 -1
  8. {portacode-1.4.18.dev0 → portacode-1.4.19}/.claude/agents/communication-manager.md +0 -0
  9. {portacode-1.4.18.dev0 → portacode-1.4.19}/.claude/settings.local.json +0 -0
  10. {portacode-1.4.18.dev0 → portacode-1.4.19}/.gitignore +0 -0
  11. {portacode-1.4.18.dev0 → portacode-1.4.19}/.gitmodules +0 -0
  12. {portacode-1.4.18.dev0 → portacode-1.4.19}/LICENSE +0 -0
  13. {portacode-1.4.18.dev0 → portacode-1.4.19}/MANIFEST.in +0 -0
  14. {portacode-1.4.18.dev0 → portacode-1.4.19}/Makefile +0 -0
  15. {portacode-1.4.18.dev0 → portacode-1.4.19}/README.md +0 -0
  16. {portacode-1.4.18.dev0 → portacode-1.4.19}/backup.sh +0 -0
  17. {portacode-1.4.18.dev0 → portacode-1.4.19}/connect.py +0 -0
  18. {portacode-1.4.18.dev0 → portacode-1.4.19}/connect.sh +0 -0
  19. {portacode-1.4.18.dev0 → portacode-1.4.19}/docker-compose.yaml +0 -0
  20. {portacode-1.4.18.dev0 → portacode-1.4.19}/docs/images/device-transfer-button.png +0 -0
  21. {portacode-1.4.18.dev0 → portacode-1.4.19}/docs/images/device-transfer-modal.png +0 -0
  22. {portacode-1.4.18.dev0 → portacode-1.4.19}/docs/images/pair-device-button.png +0 -0
  23. {portacode-1.4.18.dev0 → portacode-1.4.19}/docs/images/pairing-request.png +0 -0
  24. {portacode-1.4.18.dev0 → portacode-1.4.19}/docs/images/student-workspace.png +0 -0
  25. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/README.md +0 -0
  26. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/simple_device/Dockerfile +0 -0
  27. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/simple_device/README.md +0 -0
  28. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode +0 -0
  29. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/simple_device/data/device-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  30. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/simple_device/docker-compose.yaml +0 -0
  31. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/Dockerfile +0 -0
  32. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/README.md +0 -0
  33. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode +0 -0
  34. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/.local/share/portacode/keys/id_portacode.pub +0 -0
  35. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/.local/share/portacode/run/gateway.pid +0 -0
  36. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/.gitignore +0 -0
  37. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/README.md +0 -0
  38. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/db.sqlite3 +0 -0
  39. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__init__.py +0 -0
  40. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/__init__.cpython-311.pyc +0 -0
  41. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/settings.cpython-311.pyc +0 -0
  42. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/urls.cpython-311.pyc +0 -0
  43. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/__pycache__/wsgi.cpython-311.pyc +0 -0
  44. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/asgi.py +0 -0
  45. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/settings.py +0 -0
  46. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/urls.py +0 -0
  47. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/galactic_bakeshop/wsgi.py +0 -0
  48. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/manage.py +0 -0
  49. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/requirements.txt +0 -0
  50. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/templates/treats/home.html +0 -0
  51. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__init__.py +0 -0
  52. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/__init__.cpython-311.pyc +0 -0
  53. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/admin.cpython-311.pyc +0 -0
  54. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/apps.cpython-311.pyc +0 -0
  55. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/menu.cpython-311.pyc +0 -0
  56. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/models.cpython-311.pyc +0 -0
  57. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/urls.cpython-311.pyc +0 -0
  58. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/__pycache__/views.cpython-311.pyc +0 -0
  59. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/admin.py +0 -0
  60. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/apps.py +0 -0
  61. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/menu.py +0 -0
  62. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__init__.py +0 -0
  63. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/migrations/__pycache__/__init__.cpython-311.pyc +0 -0
  64. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/models.py +0 -0
  65. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/tests.py +0 -0
  66. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/urls.py +0 -0
  67. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-01/workspace/treats/views.py +0 -0
  68. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode +0 -0
  69. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/.local/share/portacode/keys/id_portacode.pub +0 -0
  70. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/README.md +0 -0
  71. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/__init__.py +0 -0
  72. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/asgi.py +0 -0
  73. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/settings.py +0 -0
  74. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/urls.py +0 -0
  75. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/galactic_bakeshop/wsgi.py +0 -0
  76. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/manage.py +0 -0
  77. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/requirements.txt +0 -0
  78. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/templates/treats/home.html +0 -0
  79. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/__init__.py +0 -0
  80. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/admin.py +0 -0
  81. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/apps.py +0 -0
  82. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/menu.py +0 -0
  83. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/migrations/__init__.py +0 -0
  84. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/models.py +0 -0
  85. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/tests.py +0 -0
  86. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/urls.py +0 -0
  87. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-02/workspace/treats/views.py +0 -0
  88. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode +0 -0
  89. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/.local/share/portacode/keys/id_portacode.pub +0 -0
  90. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/README.md +0 -0
  91. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/__init__.py +0 -0
  92. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/asgi.py +0 -0
  93. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/settings.py +0 -0
  94. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/urls.py +0 -0
  95. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/galactic_bakeshop/wsgi.py +0 -0
  96. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/manage.py +0 -0
  97. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/requirements.txt +0 -0
  98. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/templates/treats/home.html +0 -0
  99. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/__init__.py +0 -0
  100. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/admin.py +0 -0
  101. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/apps.py +0 -0
  102. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/menu.py +0 -0
  103. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/migrations/__init__.py +0 -0
  104. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/models.py +0 -0
  105. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/tests.py +0 -0
  106. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/urls.py +0 -0
  107. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-03/workspace/treats/views.py +0 -0
  108. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode +0 -0
  109. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/.local/share/portacode/keys/id_portacode.pub +0 -0
  110. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/README.md +0 -0
  111. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/__init__.py +0 -0
  112. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/asgi.py +0 -0
  113. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/settings.py +0 -0
  114. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/urls.py +0 -0
  115. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/galactic_bakeshop/wsgi.py +0 -0
  116. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/manage.py +0 -0
  117. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/requirements.txt +0 -0
  118. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/templates/treats/home.html +0 -0
  119. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/__init__.py +0 -0
  120. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/admin.py +0 -0
  121. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/apps.py +0 -0
  122. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/menu.py +0 -0
  123. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/migrations/__init__.py +0 -0
  124. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/models.py +0 -0
  125. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/tests.py +0 -0
  126. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/urls.py +0 -0
  127. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-04/workspace/treats/views.py +0 -0
  128. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode +0 -0
  129. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/.local/share/portacode/keys/id_portacode.pub +0 -0
  130. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/README.md +0 -0
  131. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/__init__.py +0 -0
  132. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/asgi.py +0 -0
  133. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/settings.py +0 -0
  134. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/urls.py +0 -0
  135. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/galactic_bakeshop/wsgi.py +0 -0
  136. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/manage.py +0 -0
  137. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/requirements.txt +0 -0
  138. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/templates/treats/home.html +0 -0
  139. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/__init__.py +0 -0
  140. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/admin.py +0 -0
  141. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/apps.py +0 -0
  142. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/menu.py +0 -0
  143. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/migrations/__init__.py +0 -0
  144. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/models.py +0 -0
  145. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/tests.py +0 -0
  146. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/urls.py +0 -0
  147. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-05/workspace/treats/views.py +0 -0
  148. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode +0 -0
  149. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/.local/share/portacode/keys/id_portacode.pub +0 -0
  150. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/README.md +0 -0
  151. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/__init__.py +0 -0
  152. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/asgi.py +0 -0
  153. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/settings.py +0 -0
  154. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/urls.py +0 -0
  155. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/galactic_bakeshop/wsgi.py +0 -0
  156. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/manage.py +0 -0
  157. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/requirements.txt +0 -0
  158. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/templates/treats/home.html +0 -0
  159. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/__init__.py +0 -0
  160. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/admin.py +0 -0
  161. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/apps.py +0 -0
  162. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/menu.py +0 -0
  163. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/migrations/__init__.py +0 -0
  164. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/models.py +0 -0
  165. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/tests.py +0 -0
  166. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/urls.py +0 -0
  167. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-06/workspace/treats/views.py +0 -0
  168. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode +0 -0
  169. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/.local/share/portacode/keys/id_portacode.pub +0 -0
  170. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/README.md +0 -0
  171. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/__init__.py +0 -0
  172. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/asgi.py +0 -0
  173. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/settings.py +0 -0
  174. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/urls.py +0 -0
  175. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/galactic_bakeshop/wsgi.py +0 -0
  176. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/manage.py +0 -0
  177. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/requirements.txt +0 -0
  178. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/templates/treats/home.html +0 -0
  179. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/__init__.py +0 -0
  180. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/admin.py +0 -0
  181. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/apps.py +0 -0
  182. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/menu.py +0 -0
  183. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/migrations/__init__.py +0 -0
  184. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/models.py +0 -0
  185. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/tests.py +0 -0
  186. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/urls.py +0 -0
  187. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-07/workspace/treats/views.py +0 -0
  188. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode +0 -0
  189. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/.local/share/portacode/keys/id_portacode.pub +0 -0
  190. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/README.md +0 -0
  191. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/__init__.py +0 -0
  192. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/asgi.py +0 -0
  193. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/settings.py +0 -0
  194. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/urls.py +0 -0
  195. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/galactic_bakeshop/wsgi.py +0 -0
  196. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/manage.py +0 -0
  197. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/requirements.txt +0 -0
  198. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/templates/treats/home.html +0 -0
  199. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/__init__.py +0 -0
  200. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/admin.py +0 -0
  201. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/apps.py +0 -0
  202. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/menu.py +0 -0
  203. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/migrations/__init__.py +0 -0
  204. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/models.py +0 -0
  205. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/tests.py +0 -0
  206. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/urls.py +0 -0
  207. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-08/workspace/treats/views.py +0 -0
  208. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode +0 -0
  209. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/.local/share/portacode/keys/id_portacode.pub +0 -0
  210. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/README.md +0 -0
  211. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/__init__.py +0 -0
  212. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/asgi.py +0 -0
  213. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/settings.py +0 -0
  214. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/urls.py +0 -0
  215. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/galactic_bakeshop/wsgi.py +0 -0
  216. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/manage.py +0 -0
  217. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/requirements.txt +0 -0
  218. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/templates/treats/home.html +0 -0
  219. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/__init__.py +0 -0
  220. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/admin.py +0 -0
  221. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/apps.py +0 -0
  222. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/menu.py +0 -0
  223. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/migrations/__init__.py +0 -0
  224. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/models.py +0 -0
  225. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/tests.py +0 -0
  226. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/urls.py +0 -0
  227. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-09/workspace/treats/views.py +0 -0
  228. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode +0 -0
  229. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/.local/share/portacode/keys/id_portacode.pub +0 -0
  230. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/README.md +0 -0
  231. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/__init__.py +0 -0
  232. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/asgi.py +0 -0
  233. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/settings.py +0 -0
  234. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/urls.py +0 -0
  235. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/galactic_bakeshop/wsgi.py +0 -0
  236. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/manage.py +0 -0
  237. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/requirements.txt +0 -0
  238. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/templates/treats/home.html +0 -0
  239. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/__init__.py +0 -0
  240. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/admin.py +0 -0
  241. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/apps.py +0 -0
  242. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/menu.py +0 -0
  243. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/migrations/__init__.py +0 -0
  244. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/models.py +0 -0
  245. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/tests.py +0 -0
  246. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/urls.py +0 -0
  247. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/data/student-10/workspace/treats/views.py +0 -0
  248. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/docker-compose.yaml +0 -0
  249. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/README.md +0 -0
  250. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/galactic_bakeshop/__init__.py +0 -0
  251. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/galactic_bakeshop/asgi.py +0 -0
  252. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/galactic_bakeshop/settings.py +0 -0
  253. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/galactic_bakeshop/urls.py +0 -0
  254. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/galactic_bakeshop/wsgi.py +0 -0
  255. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/manage.py +0 -0
  256. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/requirements.txt +0 -0
  257. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/templates/treats/home.html +0 -0
  258. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/__init__.py +0 -0
  259. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/admin.py +0 -0
  260. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/apps.py +0 -0
  261. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/menu.py +0 -0
  262. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/migrations/__init__.py +0 -0
  263. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/models.py +0 -0
  264. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/tests.py +0 -0
  265. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/urls.py +0 -0
  266. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/initial_content/treats/views.py +0 -0
  267. {portacode-1.4.18.dev0 → portacode-1.4.19}/examples/workshop_fleet/instructions/WELCOME.md +0 -0
  268. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/README.md +0 -0
  269. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/__init__.py +0 -0
  270. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/__main__.py +0 -0
  271. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/cli.py +0 -0
  272. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/README.md +0 -0
  273. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/__init__.py +0 -0
  274. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/client.py +0 -0
  275. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/README.md +0 -0
  276. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/__init__.py +0 -0
  277. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/chunked_content.py +0 -0
  278. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/diff_handlers.py +0 -0
  279. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/file_handlers.py +0 -0
  280. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_aware_file_handlers.py +0 -0
  281. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/README.md +0 -0
  282. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/__init__.py +0 -0
  283. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/file_system_watcher.py +0 -0
  284. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/git_manager.py +0 -0
  285. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/handlers.py +0 -0
  286. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/manager.py +0 -0
  287. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/models.py +0 -0
  288. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state/utils.py +0 -0
  289. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/project_state_handlers.py +0 -0
  290. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/session.py +0 -0
  291. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/system_handlers.py +0 -0
  292. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/tab_factory.py +0 -0
  293. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/terminal_handlers.py +0 -0
  294. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/test_proxmox_infra.py +0 -0
  295. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/handlers/update_handler.py +0 -0
  296. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/multiplex.py +0 -0
  297. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/connection/terminal.py +0 -0
  298. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/data.py +0 -0
  299. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/keypair.py +0 -0
  300. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/__init__.py +0 -0
  301. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/__pycache__/__init__.cpython-311.pyc +0 -0
  302. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/__pycache__/link_capture_wrapper.cpython-311.pyc +0 -0
  303. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/elinks +0 -0
  304. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/gio-open +0 -0
  305. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/gnome-open +0 -0
  306. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/gvfs-open +0 -0
  307. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/kde-open +0 -0
  308. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/kfmclient +0 -0
  309. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/link_capture_exec.sh +0 -0
  310. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/link_capture_wrapper.py +0 -0
  311. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/links +0 -0
  312. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/links2 +0 -0
  313. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/lynx +0 -0
  314. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/mate-open +0 -0
  315. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/netsurf +0 -0
  316. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/sensible-browser +0 -0
  317. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/w3m +0 -0
  318. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/x-www-browser +0 -0
  319. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/link_capture/bin/xdg-open +0 -0
  320. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/logging_categories.py +0 -0
  321. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/pairing.py +0 -0
  322. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/service.py +0 -0
  323. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/static/js/test-ntp-clock.html +0 -0
  324. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/static/js/utils/ntp-clock.js +0 -0
  325. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/utils/NTP_ARCHITECTURE.md +0 -0
  326. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/utils/__init__.py +0 -0
  327. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/utils/diff_apply.py +0 -0
  328. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/utils/diff_renderer.py +0 -0
  329. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode/utils/ntp_clock.py +0 -0
  330. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/SOURCES.txt +0 -0
  331. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/dependency_links.txt +0 -0
  332. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/entry_points.txt +0 -0
  333. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/requires.txt +0 -0
  334. {portacode-1.4.18.dev0 → portacode-1.4.19}/portacode.egg-info/top_level.txt +0 -0
  335. {portacode-1.4.18.dev0 → portacode-1.4.19}/pyproject.toml +0 -0
  336. {portacode-1.4.18.dev0 → portacode-1.4.19}/restore.sh +0 -0
  337. {portacode-1.4.18.dev0 → portacode-1.4.19}/run_tests.py +0 -0
  338. {portacode-1.4.18.dev0 → portacode-1.4.19}/setup.cfg +0 -0
  339. {portacode-1.4.18.dev0 → portacode-1.4.19}/setup.py +0 -0
  340. {portacode-1.4.18.dev0 → portacode-1.4.19}/test.sh +0 -0
  341. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/README.md +0 -0
  342. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/__init__.py +0 -0
  343. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_device_online.py +0 -0
  344. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_file_operations.py +0 -0
  345. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_git_status_ui.py +0 -0
  346. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_login_flow.py +0 -0
  347. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_navigate_testing_folder.py +0 -0
  348. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_play_store_screenshots.py +0 -0
  349. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_terminal_buffer_performance.py +0 -0
  350. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_terminal_interaction.py +0 -0
  351. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_terminal_loading_race_condition.py +0 -0
  352. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_modules/test_terminal_start.py +0 -0
  353. {portacode-1.4.18.dev0 → portacode-1.4.19}/test_request_id.py +0 -0
  354. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/.env.example +0 -0
  355. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/README.md +0 -0
  356. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/__init__.py +0 -0
  357. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/cli.py +0 -0
  358. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/__init__.py +0 -0
  359. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/base_test.py +0 -0
  360. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/cli_manager.py +0 -0
  361. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/hierarchical_runner.py +0 -0
  362. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/playwright_manager.py +0 -0
  363. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/runner.py +0 -0
  364. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/shared_cli_manager.py +0 -0
  365. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/core/test_discovery.py +0 -0
  366. {portacode-1.4.18.dev0 → portacode-1.4.19}/testing_framework/requirements.txt +0 -0
  367. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/UI_UX/opening_a_file_on_desktop_results_in_nothing.md +0 -0
  368. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/agent_context_management.md +0 -0
  369. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/django_server_time_sync.md +0 -0
  370. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/device_performance_degradation.md +0 -0
  371. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/git_data_not_captured_in_proxmox.md +0 -0
  372. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/indefinite_resource_loading.md +0 -0
  373. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/portacode_service_silently_down.md +0 -0
  374. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/premature_terminal_exit.md +0 -0
  375. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/project_cpu_hotspots.md +0 -0
  376. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/terminals_exit_upon_starting.md +0 -0
  377. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/issues/wrong_item_classification_on_client_side.md +0 -0
  378. {portacode-1.4.18.dev0 → portacode-1.4.19}/todo/smartphone_terminal_input_frustrations.md +0 -0
  379. {portacode-1.4.18.dev0 → portacode-1.4.19}/tools/generate_play_store_assets.py +0 -0
  380. {portacode-1.4.18.dev0 → portacode-1.4.19}/tools/pairing_tester.py +0 -0
  381. {portacode-1.4.18.dev0 → portacode-1.4.19}/tools/run_screenshot_suite.sh +0 -0
  382. {portacode-1.4.18.dev0 → portacode-1.4.19}/tools/test_python_ntp_clock.py +0 -0
  383. {portacode-1.4.18.dev0 → portacode-1.4.19}/validate.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.18.dev0
3
+ Version: 1.4.19
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '1.4.18.dev0'
32
- __version_tuple__ = version_tuple = (1, 4, 18, 'dev0')
31
+ __version__ = version = '1.4.19'
32
+ __version_tuple__ = version_tuple = (1, 4, 19)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -380,7 +380,7 @@ Starts a previously provisioned, Portacode-managed LXC container. Handled by [`S
380
380
 
381
381
  **Payload Fields:**
382
382
 
383
- * `ctid` (string, required): Identifier of the container to start.
383
+ * `ctid` (string, optional): Identifier of the container to start. If omitted, the handler resolves the CTID from the managed container records using `child_device_id`.
384
384
  * `child_device_id` (string, required): Dashboard `Device.id` of the container that triggered the request; the handler validates the CT belongs to that device before issuing the start.
385
385
 
386
386
  **Responses:**
@@ -394,7 +394,7 @@ Stops a running Portacode-managed container. Handled by [`StopProxmoxContainerHa
394
394
 
395
395
  **Payload Fields:**
396
396
 
397
- * `ctid` (string, required): Identifier of the container to stop.
397
+ * `ctid` (string, optional): Identifier of the container to stop. If omitted, the handler resolves the CTID from managed container records and, if needed, by scanning managed container descriptions for the matching `device_id` marker.
398
398
  * `child_device_id` (string, required): Dashboard `Device.id` that owns the container; the handler rejects the request if the CT is mapped to another device.
399
399
 
400
400
  **Responses:**
@@ -408,12 +408,13 @@ Deletes a managed container from Proxmox (stopping it first if necessary) and re
408
408
 
409
409
  **Payload Fields:**
410
410
 
411
- * `ctid` (string, required): Identifier of the container to delete.
411
+ * `ctid` (string, optional): Identifier of the container to delete. If omitted, the handler resolves the CTID from managed container records and, if needed, by scanning managed container descriptions for the matching `device_id` marker.
412
412
  * `child_device_id` (string, required): Dashboard `Device.id` that should own the container metadata being purged.
413
413
 
414
414
  **Responses:**
415
415
 
416
416
  * Emits a [`proxmox_container_action`](#proxmox_container_action-event) event with `action="remove"` and the refreshed infra snapshot after deletion.
417
+ * If no managed container matches the requested `child_device_id`, emits a `proxmox_container_action` event with `success=false`, `status="not_found"`, and a human-readable message (no error event).
417
418
  * Emits an [`error`](#error) event on failure.
418
419
 
419
420
  ### `proxmox_container_created`
@@ -62,16 +62,18 @@ class BaseHandler(ABC):
62
62
  # Get client session manager from context
63
63
  client_session_manager = self.context.get("client_session_manager")
64
64
 
65
+ bypass_session_gate = bool(payload.get("bypass_session_gate"))
65
66
  if client_session_manager and client_session_manager.has_interested_clients():
66
67
  # Get target sessions
67
68
  target_sessions = client_session_manager.get_target_sessions(project_id)
68
- if not target_sessions:
69
+ if not target_sessions and not bypass_session_gate:
69
70
  logger.debug("handler: No target sessions found, skipping response send")
70
71
  return
71
72
 
72
73
  # Add session targeting information
73
74
  enhanced_payload = dict(payload)
74
- enhanced_payload["client_sessions"] = target_sessions
75
+ if target_sessions:
76
+ enhanced_payload["client_sessions"] = target_sessions
75
77
 
76
78
  # Add backward compatibility reply_channel (first session if not provided)
77
79
  if not reply_channel:
@@ -89,15 +91,24 @@ class BaseHandler(ABC):
89
91
  payload["reply_channel"] = reply_channel
90
92
  await self.control_channel.send(payload)
91
93
 
92
- async def send_error(self, message: str, reply_channel: Optional[str] = None, project_id: str = None) -> None:
94
+ async def send_error(
95
+ self,
96
+ message: str,
97
+ reply_channel: Optional[str] = None,
98
+ project_id: str = None,
99
+ request_id: Optional[str] = None,
100
+ ) -> None:
93
101
  """Send an error response with client session awareness.
94
102
 
95
103
  Args:
96
104
  message: Error message
97
105
  reply_channel: Optional reply channel for backward compatibility
98
106
  project_id: Optional project filter for targeting specific sessions
107
+ request_id: Optional request_id to correlate error with a request
99
108
  """
100
109
  payload = {"event": "error", "message": message}
110
+ if request_id:
111
+ payload["request_id"] = request_id
101
112
  await self.send_response(payload, reply_channel, project_id)
102
113
 
103
114
 
@@ -163,7 +174,12 @@ class AsyncHandler(BaseHandler):
163
174
  logger.exception("handler: Error in async handler %s: %s", self.command_name, exc)
164
175
  # Extract project_id from original message for error targeting
165
176
  project_id = message.get("project_id")
166
- await self.send_error(str(exc), reply_channel, project_id)
177
+ await self.send_error(
178
+ str(exc),
179
+ reply_channel,
180
+ project_id,
181
+ request_id=message.get("request_id"),
182
+ )
167
183
 
168
184
 
169
185
  class SyncHandler(BaseHandler):
@@ -216,4 +232,9 @@ class SyncHandler(BaseHandler):
216
232
  logger.exception("Error in sync handler %s: %s", self.command_name, exc)
217
233
  # Extract project_id from original message for error targeting
218
234
  project_id = message.get("project_id")
219
- await self.send_error(str(exc), reply_channel, project_id)
235
+ await self.send_error(
236
+ str(exc),
237
+ reply_channel,
238
+ project_id,
239
+ request_id=message.get("request_id"),
240
+ )
@@ -17,6 +17,7 @@ import sys
17
17
  import tempfile
18
18
  import time
19
19
  import threading
20
+ import random
20
21
  from datetime import datetime, timezone
21
22
  from pathlib import Path
22
23
  from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
@@ -33,6 +34,44 @@ REPO_ROOT = Path(__file__).resolve().parents[3]
33
34
  NET_SETUP_SCRIPT = REPO_ROOT / "proxmox_management" / "net_setup.py"
34
35
  CONTAINERS_DIR = CONFIG_DIR / "containers"
35
36
  MANAGED_MARKER = "portacode-managed:true"
37
+ DEVICE_ID_MARKER = "device_id="
38
+ PROVISIONING_ID_MARKER = "provisioning_id="
39
+
40
+
41
+ def _sanitize_description_value(value: Any) -> str:
42
+ return str(value).strip().replace(";", "_")
43
+
44
+
45
+ def _build_managed_description(
46
+ base: Optional[str],
47
+ *,
48
+ device_id: Optional[str] = None,
49
+ provisioning_id: Optional[str] = None,
50
+ ) -> str:
51
+ parts = [part for part in (base or "").split(";") if part]
52
+ if MANAGED_MARKER not in parts:
53
+ parts.insert(0, MANAGED_MARKER)
54
+ if device_id:
55
+ device_id_value = _sanitize_description_value(device_id)
56
+ if not any(part.startswith(DEVICE_ID_MARKER) for part in parts):
57
+ parts.append(f"{DEVICE_ID_MARKER}{device_id_value}")
58
+ if provisioning_id:
59
+ provisioning_value = _sanitize_description_value(provisioning_id)
60
+ if not any(part.startswith(PROVISIONING_ID_MARKER) for part in parts):
61
+ parts.append(f"{PROVISIONING_ID_MARKER}{provisioning_value}")
62
+ return ";".join(parts)
63
+
64
+
65
+ def _extract_marker_value(description: str, prefix: str) -> Optional[str]:
66
+ for part in description.split(";"):
67
+ part = part.strip()
68
+ if part.startswith(prefix):
69
+ return part[len(prefix) :].strip()
70
+ return None
71
+
72
+
73
+ def _parse_device_id_from_description(description: str) -> Optional[str]:
74
+ return _extract_marker_value(description, DEVICE_ID_MARKER)
36
75
 
37
76
  DEFAULT_HOST = "localhost"
38
77
  DEFAULT_NODE_NAME = os.uname().nodename.split(".", 1)[0]
@@ -116,6 +155,7 @@ def _emit_progress_event(
116
155
  payload["details"] = details
117
156
  if on_behalf_of_device:
118
157
  payload["on_behalf_of_device"] = str(on_behalf_of_device)
158
+ payload["bypass_session_gate"] = True
119
159
 
120
160
  future = asyncio.run_coroutine_threadsafe(handler.send_response(payload), loop)
121
161
  future.add_done_callback(
@@ -1179,7 +1219,7 @@ def _build_bootstrap_steps(
1179
1219
  steps.append(
1180
1220
  {
1181
1221
  "name": "add_ssh_key",
1182
- "cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:{user} /home/{user}/.ssh",
1222
+ "cmd": f"install -d -m 700 /home/{user}/.ssh && echo '{ssh_key}' >> /home/{user}/.ssh/authorized_keys && chown -R {user}:$(id -gn {shlex.quote(user)}) /home/{user}/.ssh",
1183
1223
  "retries": 0,
1184
1224
  }
1185
1225
  )
@@ -1256,12 +1296,21 @@ def _validate_positive_number(value: Any, default: float) -> float:
1256
1296
 
1257
1297
 
1258
1298
  def _wait_for_task(proxmox: Any, node: str, upid: str) -> Tuple[Dict[str, Any], float]:
1299
+ from proxmoxer.core import ResourceException
1259
1300
  start = time.time()
1301
+ poll = 2.0
1260
1302
  while True:
1261
- status = proxmox.nodes(node).tasks(upid).status.get()
1303
+ try:
1304
+ status = proxmox.nodes(node).tasks(upid).status.get()
1305
+ except ResourceException as e:
1306
+ if str(e).startswith("599 "):
1307
+ # transient proxy glitch; task may still be running
1308
+ time.sleep(poll + random.random() * 0.5) # small jitter
1309
+ continue
1310
+ raise
1262
1311
  if status.get("status") == "stopped":
1263
1312
  return status, time.time() - start
1264
- time.sleep(1)
1313
+ time.sleep(poll)
1265
1314
 
1266
1315
 
1267
1316
  def _list_running_managed(proxmox: Any, node: str) -> List[Tuple[str, Dict[str, Any]]]:
@@ -1399,18 +1448,97 @@ def _parse_ctid(message: Dict[str, Any]) -> int:
1399
1448
  raise ValueError("ctid is required")
1400
1449
 
1401
1450
 
1451
+ class _DeviceLookupError(ValueError):
1452
+ pass
1453
+
1454
+
1455
+ def _resolve_vmid_for_device(device_id: str) -> int:
1456
+ _initialize_managed_containers_state()
1457
+ records = list(_MANAGED_CONTAINERS_STATE.get("records", {}).values())
1458
+ for record in records:
1459
+ record_device_id = record.get("device_id")
1460
+ if record_device_id is None:
1461
+ continue
1462
+ if str(record_device_id) != str(device_id):
1463
+ continue
1464
+ vmid = record.get("vmid")
1465
+ if vmid is None:
1466
+ continue
1467
+ try:
1468
+ return int(str(vmid).strip())
1469
+ except ValueError:
1470
+ raise ValueError("ctid must be an integer") from None
1471
+ raise _DeviceLookupError(
1472
+ f"No managed container record found for device_id {device_id!r}."
1473
+ )
1474
+
1475
+
1476
+ def _resolve_vmid_for_device_in_proxmox(
1477
+ proxmox: Any, node: str, device_id: str
1478
+ ) -> int:
1479
+ device_id_value = str(device_id).strip()
1480
+ matches: List[int] = []
1481
+ try:
1482
+ containers = proxmox.nodes(node).lxc.get()
1483
+ except Exception as exc: # pragma: no cover - proxmox failure
1484
+ raise _DeviceLookupError(
1485
+ f"Unable to query Proxmox containers for device_id {device_id_value!r}: {exc}"
1486
+ ) from exc
1487
+ for entry in containers or []:
1488
+ vmid = entry.get("vmid")
1489
+ if vmid is None:
1490
+ continue
1491
+ try:
1492
+ cfg = proxmox.nodes(node).lxc(str(vmid)).config.get()
1493
+ except Exception:
1494
+ continue
1495
+ description = (cfg or {}).get("description") or ""
1496
+ if MANAGED_MARKER not in description:
1497
+ continue
1498
+ desc_device_id = _parse_device_id_from_description(description)
1499
+ if desc_device_id is None:
1500
+ continue
1501
+ if str(desc_device_id) != device_id_value:
1502
+ continue
1503
+ matches.append(int(str(vmid).strip()))
1504
+ if not matches:
1505
+ raise _DeviceLookupError(
1506
+ f"No managed container found for device_id {device_id_value!r}. It may already be deleted."
1507
+ )
1508
+ if len(matches) > 1:
1509
+ raise _DeviceLookupError(
1510
+ f"Multiple managed containers found for device_id {device_id_value!r}: {matches}."
1511
+ )
1512
+ return matches[0]
1513
+
1514
+
1402
1515
  def _ensure_container_managed(
1403
1516
  proxmox: Any, node: str, vmid: int, *, device_id: Optional[str] = None
1404
1517
  ) -> Tuple[Dict[str, Any], Dict[str, Any]]:
1405
- record = _read_container_record(vmid)
1518
+ try:
1519
+ record = _read_container_record(vmid)
1520
+ except FileNotFoundError:
1521
+ record = {}
1406
1522
  ct_cfg = proxmox.nodes(node).lxc(str(vmid)).config.get()
1407
- if not ct_cfg or MANAGED_MARKER not in (ct_cfg.get("description") or ""):
1523
+ description = (ct_cfg or {}).get("description") or ""
1524
+ if not ct_cfg or MANAGED_MARKER not in description:
1408
1525
  raise RuntimeError(f"Container {vmid} is not managed by Portacode.")
1409
1526
  record_device_id = record.get("device_id")
1410
- if device_id and str(record_device_id or "") != str(device_id):
1411
- raise RuntimeError(
1412
- f"Container {vmid} is managed for device {record_device_id!r}, not {device_id!r}."
1413
- )
1527
+ description_device_id = _parse_device_id_from_description(description)
1528
+ if device_id:
1529
+ device_id_value = str(device_id)
1530
+ if description_device_id and str(description_device_id) != device_id_value:
1531
+ raise RuntimeError(
1532
+ f"Container {vmid} is managed for device {description_device_id!r}, not {device_id_value!r}."
1533
+ )
1534
+ if record_device_id and str(record_device_id) != device_id_value:
1535
+ raise RuntimeError(
1536
+ f"Container {vmid} is managed for device {record_device_id!r}, not {device_id_value!r}."
1537
+ )
1538
+ if not description_device_id and not record_device_id:
1539
+ raise RuntimeError(
1540
+ f"Container {vmid} is missing a device_id marker; refusing to operate without ctid."
1541
+ )
1414
1542
  return record, ct_cfg
1415
1543
 
1416
1544
 
@@ -1444,6 +1572,14 @@ def _su_command(user: str, command: str) -> str:
1444
1572
  return f"su - {user} -s /bin/sh -c {shlex.quote(command)}"
1445
1573
 
1446
1574
 
1575
+ def _resolve_user_group(vmid: int, user: str) -> str:
1576
+ res = _run_pct(vmid, f"id -gn {shlex.quote(user)}")
1577
+ group = (res.get("stdout") or "").strip()
1578
+ if group:
1579
+ return f"{user}:{group}"
1580
+ return f"{user}:{user}"
1581
+
1582
+
1447
1583
  def _resolve_portacode_cli_path(vmid: int, user: str) -> str:
1448
1584
  """Resolve the full path to the portacode CLI inside the container."""
1449
1585
  res = _run_pct(vmid, _su_command(user, "command -v portacode"))
@@ -1480,12 +1616,13 @@ def _push_bytes_to_container(
1480
1616
  ) -> None:
1481
1617
  logger.debug("Preparing to push %d bytes to container vmid=%s path=%s for user=%s", len(data), vmid, path, user)
1482
1618
  tmp_path: Optional[str] = None
1619
+ owner = _resolve_user_group(vmid, user)
1483
1620
  try:
1484
1621
  parent = Path(path).parent
1485
1622
  parent_str = parent.as_posix()
1486
1623
  if parent_str not in {"", ".", "/"}:
1487
1624
  _run_pct_exec_check(vmid, ["mkdir", "-p", parent_str])
1488
- _run_pct_exec_check(vmid, ["chown", "-R", f"{user}:{user}", parent_str])
1625
+ _run_pct_exec_check(vmid, ["chown", "-R", owner, parent_str])
1489
1626
 
1490
1627
  with tempfile.NamedTemporaryFile(delete=False) as tmp:
1491
1628
  tmp.write(data)
@@ -1497,7 +1634,7 @@ def _push_bytes_to_container(
1497
1634
  if push_res.returncode != 0:
1498
1635
  raise RuntimeError(push_res.stderr or push_res.stdout or f"pct push returned {push_res.returncode}")
1499
1636
 
1500
- _run_pct_exec_check(vmid, ["chown", f"{user}:{user}", path])
1637
+ _run_pct_exec_check(vmid, ["chown", owner, path])
1501
1638
  _run_pct_exec_check(vmid, ["chmod", format(mode, "o"), path])
1502
1639
  logger.debug("Successfully pushed %d bytes to vmid=%s path=%s", len(data), vmid, path)
1503
1640
  except Exception as exc:
@@ -1516,7 +1653,8 @@ def _resolve_portacode_key_dir(vmid: int, user: str) -> str:
1516
1653
  data_home = _run_pct_check(vmid, data_dir_cmd)["stdout"].strip()
1517
1654
  portacode_dir = f"{data_home}/portacode"
1518
1655
  _run_pct_exec_check(vmid, ["mkdir", "-p", portacode_dir])
1519
- _run_pct_exec_check(vmid, ["chown", "-R", f"{user}:{user}", portacode_dir])
1656
+ owner = _resolve_user_group(vmid, user)
1657
+ _run_pct_exec_check(vmid, ["chown", "-R", owner, portacode_dir])
1520
1658
  return f"{portacode_dir}/keys"
1521
1659
 
1522
1660
 
@@ -2085,7 +2223,11 @@ class CreateProxmoxContainerHandler(SyncHandler):
2085
2223
  payload, device_id=device_id, request_id=request_id
2086
2224
  )
2087
2225
  provisioning_id = secrets.token_hex(6)
2088
- payload["description"] = f"{payload.get('description', MANAGED_MARKER)};provisioning_id={provisioning_id}"
2226
+ payload["description"] = _build_managed_description(
2227
+ payload.get("description"),
2228
+ device_id=device_id,
2229
+ provisioning_id=provisioning_id,
2230
+ )
2089
2231
 
2090
2232
  def _provision_background() -> None:
2091
2233
  nonlocal current_step_index
@@ -2321,6 +2463,7 @@ class CreateProxmoxContainerHandler(SyncHandler):
2321
2463
  "setup_steps": steps,
2322
2464
  "device_id": device_id,
2323
2465
  "on_behalf_of_device": device_id,
2466
+ "bypass_session_gate": True,
2324
2467
  "service_installed": service_installed,
2325
2468
  "request_id": request_id,
2326
2469
  },
@@ -2347,7 +2490,6 @@ class CreateProxmoxContainerHandler(SyncHandler):
2347
2490
  "event": "proxmox_container_accepted",
2348
2491
  "success": True,
2349
2492
  "message": "Provisioning accepted; resources reserved.",
2350
- "device_id": device_id,
2351
2493
  "request_id": request_id,
2352
2494
  }
2353
2495
 
@@ -2475,13 +2617,19 @@ class StartProxmoxContainerHandler(SyncHandler):
2475
2617
  return "start_proxmox_container"
2476
2618
 
2477
2619
  def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
2478
- vmid = _parse_ctid(message)
2479
2620
  child_device_id = (message.get("child_device_id") or "").strip()
2480
2621
  if not child_device_id:
2481
2622
  raise ValueError("child_device_id is required for start_proxmox_container")
2482
2623
  config = _ensure_infra_configured()
2483
2624
  proxmox = _connect_proxmox(config)
2484
2625
  node = _get_node_from_config(config)
2626
+ try:
2627
+ vmid = _parse_ctid(message)
2628
+ except ValueError:
2629
+ try:
2630
+ vmid = _resolve_vmid_for_device(child_device_id)
2631
+ except _DeviceLookupError:
2632
+ vmid = _resolve_vmid_for_device_in_proxmox(proxmox, node, child_device_id)
2485
2633
  _ensure_container_managed(proxmox, node, vmid, device_id=child_device_id)
2486
2634
 
2487
2635
  status, elapsed = _start_container(proxmox, node, vmid)
@@ -2508,13 +2656,19 @@ class StopProxmoxContainerHandler(SyncHandler):
2508
2656
  return "stop_proxmox_container"
2509
2657
 
2510
2658
  def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
2511
- vmid = _parse_ctid(message)
2512
2659
  child_device_id = (message.get("child_device_id") or "").strip()
2513
2660
  if not child_device_id:
2514
2661
  raise ValueError("child_device_id is required for stop_proxmox_container")
2515
2662
  config = _ensure_infra_configured()
2516
2663
  proxmox = _connect_proxmox(config)
2517
2664
  node = _get_node_from_config(config)
2665
+ try:
2666
+ vmid = _parse_ctid(message)
2667
+ except ValueError:
2668
+ try:
2669
+ vmid = _resolve_vmid_for_device(child_device_id)
2670
+ except _DeviceLookupError:
2671
+ vmid = _resolve_vmid_for_device_in_proxmox(proxmox, node, child_device_id)
2518
2672
  _ensure_container_managed(proxmox, node, vmid, device_id=child_device_id)
2519
2673
 
2520
2674
  status, elapsed = _stop_container(proxmox, node, vmid)
@@ -2547,13 +2701,33 @@ class RemoveProxmoxContainerHandler(SyncHandler):
2547
2701
  return "remove_proxmox_container"
2548
2702
 
2549
2703
  def execute(self, message: Dict[str, Any]) -> Dict[str, Any]:
2550
- vmid = _parse_ctid(message)
2551
2704
  child_device_id = (message.get("child_device_id") or "").strip()
2552
2705
  if not child_device_id:
2553
2706
  raise ValueError("child_device_id is required for remove_proxmox_container")
2707
+ request_id = message.get("request_id")
2554
2708
  config = _ensure_infra_configured()
2555
2709
  proxmox = _connect_proxmox(config)
2556
2710
  node = _get_node_from_config(config)
2711
+ try:
2712
+ vmid = _parse_ctid(message)
2713
+ except ValueError:
2714
+ try:
2715
+ vmid = _resolve_vmid_for_device(child_device_id)
2716
+ except _DeviceLookupError:
2717
+ try:
2718
+ vmid = _resolve_vmid_for_device_in_proxmox(proxmox, node, child_device_id)
2719
+ except _DeviceLookupError as exc:
2720
+ infra = get_infra_snapshot()
2721
+ return {
2722
+ "event": "proxmox_container_action",
2723
+ "action": "remove",
2724
+ "success": False,
2725
+ "message": str(exc),
2726
+ "status": "not_found",
2727
+ "child_device_id": child_device_id,
2728
+ "request_id": request_id,
2729
+ "infra": infra,
2730
+ }
2557
2731
  _ensure_container_managed(proxmox, node, vmid, device_id=child_device_id)
2558
2732
 
2559
2733
  stop_status, stop_elapsed = _stop_container(proxmox, node, vmid)
@@ -2572,6 +2746,8 @@ class RemoveProxmoxContainerHandler(SyncHandler):
2572
2746
  "delete_exitstatus": delete_status.get("exitstatus"),
2573
2747
  },
2574
2748
  "status": "deleted",
2749
+ "child_device_id": child_device_id,
2750
+ "request_id": request_id,
2575
2751
  "infra": infra,
2576
2752
  }
2577
2753
 
@@ -106,12 +106,25 @@ class CommandRegistry:
106
106
  except Exception as exc:
107
107
  logger.exception("registry: Error dispatching command %s: %s", command_name, exc)
108
108
  # Send session-aware error response
109
- await self._send_session_aware_error(str(exc), reply_channel, message.get("project_id"))
109
+ await self._send_session_aware_error(
110
+ str(exc),
111
+ reply_channel,
112
+ message.get("project_id"),
113
+ request_id=message.get("request_id"),
114
+ )
110
115
  return False
111
116
 
112
- async def _send_session_aware_error(self, message: str, reply_channel: Optional[str] = None, project_id: str = None) -> None:
117
+ async def _send_session_aware_error(
118
+ self,
119
+ message: str,
120
+ reply_channel: Optional[str] = None,
121
+ project_id: str = None,
122
+ request_id: Optional[str] = None,
123
+ ) -> None:
113
124
  """Send an error response with client session awareness."""
114
125
  error_payload = {"event": "error", "message": message}
126
+ if request_id:
127
+ error_payload["request_id"] = request_id
115
128
 
116
129
  # Get client session manager from context
117
130
  client_session_manager = self.context.get("client_session_manager")
@@ -151,4 +164,4 @@ class CommandRegistry:
151
164
 
152
165
  # Update context for all existing handlers
153
166
  for handler in self._handlers.values():
154
- handler.context = self.context
167
+ handler.context = self.context
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.18.dev0
3
+ Version: 1.4.19
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes