codex-partner 0.0.5__tar.gz → 0.0.7__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 (324) hide show
  1. {codex_partner-0.0.5 → codex_partner-0.0.7}/PKG-INFO +58 -5
  2. {codex_partner-0.0.5 → codex_partner-0.0.7}/README.md +54 -4
  3. {codex_partner-0.0.5 → codex_partner-0.0.7}/app.py +222 -22
  4. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/__init__.py +1 -1
  5. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/app_server.py +4 -1
  6. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/database.py +5 -4
  7. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/discovery.py +4 -2
  8. codex_partner-0.0.7/codex_partner/platform_support.py +74 -0
  9. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/schemas.py +1 -0
  10. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/ssh_auth.py +6 -1
  11. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/PKG-INFO +58 -5
  12. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/SOURCES.txt +2 -0
  13. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/requires.txt +3 -0
  14. {codex_partner-0.0.5 → codex_partner-0.0.7}/pyproject.toml +4 -1
  15. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/app.js +27 -4
  16. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/core.js +16 -5
  17. codex_partner-0.0.7/tests/test_platform_support.py +62 -0
  18. {codex_partner-0.0.5 → codex_partner-0.0.7}/tests/test_running_state.py +114 -25
  19. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/__main__.py +0 -0
  20. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/activity_graph.py +0 -0
  21. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/cli.py +0 -0
  22. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner/commands.py +0 -0
  23. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/dependency_links.txt +0 -0
  24. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/entry_points.txt +0 -0
  25. {codex_partner-0.0.5 → codex_partner-0.0.7}/codex_partner.egg-info/top_level.txt +0 -0
  26. {codex_partner-0.0.5 → codex_partner-0.0.7}/setup.cfg +0 -0
  27. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/__init__.py +0 -0
  28. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/chat-worker.js +0 -0
  29. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/conversation.js +0 -0
  30. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/default-user-avatar.webp +0 -0
  31. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/editor.js +0 -0
  32. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/exploration-tree.js +0 -0
  33. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/history-pagination.js +0 -0
  34. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/index.html +0 -0
  35. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/mascot-dance.css +0 -0
  36. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/mascot-dance.js +0 -0
  37. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/settings.js +0 -0
  38. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/styles.css +0 -0
  39. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/LICENSE +0 -0
  40. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/ace.js +0 -0
  41. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/ext-modelist.js +0 -0
  42. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-abap.js +0 -0
  43. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-abc.js +0 -0
  44. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-actionscript.js +0 -0
  45. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ada.js +0 -0
  46. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-alda.js +0 -0
  47. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-apache_conf.js +0 -0
  48. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-apex.js +0 -0
  49. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-applescript.js +0 -0
  50. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-aql.js +0 -0
  51. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-asciidoc.js +0 -0
  52. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-asl.js +0 -0
  53. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-assembly_arm32.js +0 -0
  54. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-assembly_x86.js +0 -0
  55. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-astro.js +0 -0
  56. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-autohotkey.js +0 -0
  57. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-basic.js +0 -0
  58. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-batchfile.js +0 -0
  59. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-bibtex.js +0 -0
  60. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-c9search.js +0 -0
  61. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-c_cpp.js +0 -0
  62. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-cedar.js +0 -0
  63. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-cedarschema.js +0 -0
  64. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-cirru.js +0 -0
  65. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-clojure.js +0 -0
  66. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-clue.js +0 -0
  67. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-cobol.js +0 -0
  68. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-coffee.js +0 -0
  69. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-coldfusion.js +0 -0
  70. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-crystal.js +0 -0
  71. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csharp.js +0 -0
  72. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csound_document.js +0 -0
  73. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csound_orchestra.js +0 -0
  74. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csound_score.js +0 -0
  75. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csp.js +0 -0
  76. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-css.js +0 -0
  77. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-csv.js +0 -0
  78. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-curly.js +0 -0
  79. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-cuttlefish.js +0 -0
  80. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-d.js +0 -0
  81. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-dart.js +0 -0
  82. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-diff.js +0 -0
  83. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-django.js +0 -0
  84. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-dockerfile.js +0 -0
  85. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-dot.js +0 -0
  86. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-drools.js +0 -0
  87. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-edifact.js +0 -0
  88. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-eiffel.js +0 -0
  89. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ejs.js +0 -0
  90. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-elixir.js +0 -0
  91. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-elm.js +0 -0
  92. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-erlang.js +0 -0
  93. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-flix.js +0 -0
  94. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-forth.js +0 -0
  95. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-fortran.js +0 -0
  96. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-fsharp.js +0 -0
  97. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-fsl.js +0 -0
  98. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ftl.js +0 -0
  99. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-gcode.js +0 -0
  100. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-gherkin.js +0 -0
  101. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-gitignore.js +0 -0
  102. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-glsl.js +0 -0
  103. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-gobstones.js +0 -0
  104. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-golang.js +0 -0
  105. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-graphqlschema.js +0 -0
  106. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-groovy.js +0 -0
  107. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-groq.js +0 -0
  108. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-haml.js +0 -0
  109. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-handlebars.js +0 -0
  110. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-haskell.js +0 -0
  111. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-haskell_cabal.js +0 -0
  112. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-haxe.js +0 -0
  113. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-hjson.js +0 -0
  114. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-html.js +0 -0
  115. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-html_elixir.js +0 -0
  116. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-html_ruby.js +0 -0
  117. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ini.js +0 -0
  118. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-io.js +0 -0
  119. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ion.js +0 -0
  120. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jack.js +0 -0
  121. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jade.js +0 -0
  122. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-java.js +0 -0
  123. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-javascript.js +0 -0
  124. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jexl.js +0 -0
  125. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-json.js +0 -0
  126. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-json5.js +0 -0
  127. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jsoniq.js +0 -0
  128. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jsp.js +0 -0
  129. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jssm.js +0 -0
  130. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-jsx.js +0 -0
  131. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-julia.js +0 -0
  132. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-kotlin.js +0 -0
  133. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-latex.js +0 -0
  134. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-latte.js +0 -0
  135. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-less.js +0 -0
  136. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-liquid.js +0 -0
  137. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-lisp.js +0 -0
  138. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-livescript.js +0 -0
  139. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-logiql.js +0 -0
  140. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-logtalk.js +0 -0
  141. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-lsl.js +0 -0
  142. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-lua.js +0 -0
  143. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-luapage.js +0 -0
  144. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-lucene.js +0 -0
  145. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-makefile.js +0 -0
  146. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mariadb.js +0 -0
  147. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-markdown.js +0 -0
  148. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mask.js +0 -0
  149. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-matlab.js +0 -0
  150. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-maze.js +0 -0
  151. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mediawiki.js +0 -0
  152. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mel.js +0 -0
  153. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mips.js +0 -0
  154. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mixal.js +0 -0
  155. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mushcode.js +0 -0
  156. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-mysql.js +0 -0
  157. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nasal.js +0 -0
  158. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nginx.js +0 -0
  159. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nim.js +0 -0
  160. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nix.js +0 -0
  161. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nsis.js +0 -0
  162. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-nunjucks.js +0 -0
  163. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-objectivec.js +0 -0
  164. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ocaml.js +0 -0
  165. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-odin.js +0 -0
  166. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-partiql.js +0 -0
  167. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-pascal.js +0 -0
  168. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-perl.js +0 -0
  169. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-pgsql.js +0 -0
  170. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-php.js +0 -0
  171. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-php_laravel_blade.js +0 -0
  172. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-pig.js +0 -0
  173. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-plain_text.js +0 -0
  174. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-plsql.js +0 -0
  175. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-powershell.js +0 -0
  176. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-praat.js +0 -0
  177. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-prisma.js +0 -0
  178. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-prolog.js +0 -0
  179. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-properties.js +0 -0
  180. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-protobuf.js +0 -0
  181. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-prql.js +0 -0
  182. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-puppet.js +0 -0
  183. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-python.js +0 -0
  184. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-qml.js +0 -0
  185. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-r.js +0 -0
  186. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-raku.js +0 -0
  187. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-razor.js +0 -0
  188. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-rdoc.js +0 -0
  189. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-red.js +0 -0
  190. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-redshift.js +0 -0
  191. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-rhtml.js +0 -0
  192. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-robot.js +0 -0
  193. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-rst.js +0 -0
  194. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-ruby.js +0 -0
  195. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-rust.js +0 -0
  196. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sac.js +0 -0
  197. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sass.js +0 -0
  198. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-scad.js +0 -0
  199. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-scala.js +0 -0
  200. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-scheme.js +0 -0
  201. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-scrypt.js +0 -0
  202. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-scss.js +0 -0
  203. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sh.js +0 -0
  204. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sjs.js +0 -0
  205. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-slim.js +0 -0
  206. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-smarty.js +0 -0
  207. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-smithy.js +0 -0
  208. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-snippets.js +0 -0
  209. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-soy_template.js +0 -0
  210. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-space.js +0 -0
  211. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sparql.js +0 -0
  212. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sql.js +0 -0
  213. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-sqlserver.js +0 -0
  214. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-stylus.js +0 -0
  215. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-svg.js +0 -0
  216. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-swift.js +0 -0
  217. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-tcl.js +0 -0
  218. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-terraform.js +0 -0
  219. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-tex.js +0 -0
  220. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-text.js +0 -0
  221. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-textile.js +0 -0
  222. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-toml.js +0 -0
  223. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-tsv.js +0 -0
  224. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-tsx.js +0 -0
  225. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-turtle.js +0 -0
  226. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-twig.js +0 -0
  227. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-typescript.js +0 -0
  228. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-vala.js +0 -0
  229. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-vbscript.js +0 -0
  230. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-velocity.js +0 -0
  231. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-verilog.js +0 -0
  232. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-vhdl.js +0 -0
  233. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-visualforce.js +0 -0
  234. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-vue.js +0 -0
  235. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-wollok.js +0 -0
  236. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-xml.js +0 -0
  237. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-xquery.js +0 -0
  238. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-yaml.js +0 -0
  239. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-zeek.js +0 -0
  240. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/mode-zig.js +0 -0
  241. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/ace/theme-tomorrow_night_eighties.js +0 -0
  242. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/i18next/LICENSE +0 -0
  243. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/i18next/i18next.min.js +0 -0
  244. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/LICENSE +0 -0
  245. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
  246. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
  247. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  248. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  249. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  250. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  251. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  252. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  253. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  254. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  255. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  256. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  257. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  258. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  259. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  260. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
  261. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Bold.woff +0 -0
  262. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  263. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  264. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  265. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  266. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
  267. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Italic.woff +0 -0
  268. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  269. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
  270. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Regular.woff +0 -0
  271. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  272. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  273. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  274. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  275. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
  276. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-Italic.woff +0 -0
  277. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  278. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  279. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  280. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  281. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  282. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  283. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  284. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  285. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  286. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  287. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
  288. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Script-Regular.woff +0 -0
  289. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  290. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
  291. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
  292. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  293. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
  294. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
  295. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  296. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
  297. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
  298. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  299. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
  300. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
  301. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  302. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  303. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  304. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  305. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/katex.min.css +0 -0
  306. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/katex/katex.min.js +0 -0
  307. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/markdown-it/LICENSE +0 -0
  308. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/markdown-it/markdown-it.min.js +0 -0
  309. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/markdown-it-texmath/LICENSE +0 -0
  310. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/markdown-it-texmath/texmath.js +0 -0
  311. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/toastui-editor/LICENSE +0 -0
  312. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/toastui-editor/toastui-editor-dark.css +0 -0
  313. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/toastui-editor/toastui-editor.css +0 -0
  314. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/toastui-editor/toastui-editor.js +0 -0
  315. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/toastui-editor/zh-cn.js +0 -0
  316. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/LICENSE +0 -0
  317. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/fit.LICENSE +0 -0
  318. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/fit.js +0 -0
  319. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/unicode11.LICENSE +0 -0
  320. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/unicode11.js +0 -0
  321. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/web-links.LICENSE +0 -0
  322. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/addons/web-links.js +0 -0
  323. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/xterm.css +0 -0
  324. {codex_partner-0.0.5 → codex_partner-0.0.7}/static/vendor/xterm/xterm.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-partner
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: A self-hosted web interface for managing persistent Codex tasks.
5
5
  Project-URL: Homepage, https://github.com/froststeam/Codex-Partner
6
6
  Project-URL: Repository, https://github.com/froststeam/Codex-Partner.git
@@ -9,6 +9,8 @@ Keywords: codex,agent,task-management,self-hosted
9
9
  Classifier: Development Status :: 3 - Alpha
10
10
  Classifier: Environment :: Web Environment
11
11
  Classifier: Framework :: FastAPI
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows
12
14
  Classifier: Operating System :: POSIX :: Linux
13
15
  Classifier: Programming Language :: Python :: 3
14
16
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -20,6 +22,7 @@ Requires-Dist: uvicorn[standard]>=0.27
20
22
  Requires-Dist: tomli>=2.0; python_version < "3.11"
21
23
  Requires-Dist: PyYAML>=6.0
22
24
  Requires-Dist: pexpect>=4.8; platform_system != "Windows"
25
+ Requires-Dist: pywinpty>=2.0.13; platform_system == "Windows"
23
26
  Requires-Dist: python-dotenv>=1.0
24
27
  Requires-Dist: Pillow>=10.0
25
28
 
@@ -45,6 +48,19 @@ Codex Partner is a self-hosted control panel for long-running Codex work. It kee
45
48
  - Model and API provider management with health checks and failover
46
49
  - Codex memories, Skills, archive, recycle bin, and SSH workspace support
47
50
  - Default YOLO mode for newly created sessions
51
+ - Safe terminal-to-web takeover: normal web sends ask before stopping a terminal turn; `Alt+Enter` keeps the message queued without interruption
52
+
53
+ ## Platform support
54
+
55
+ Codex Partner runs natively on Linux, macOS, and Windows with Python 3.10 or newer.
56
+
57
+ | Platform | Codex sessions | Workspace tools | Browser terminal | Background service |
58
+ | --- | --- | --- | --- | --- |
59
+ | Linux | Yes | Yes | POSIX PTY | systemd user service |
60
+ | macOS | Yes | Yes | zsh/bash PTY | Run directly or use launchd |
61
+ | Windows 10/11 | Yes | Yes | PowerShell through ConPTY | Run directly or use Task Scheduler |
62
+
63
+ Windows terminal support uses `pywinpty`, which is installed automatically. Key-based remote SSH works when the OpenSSH client is installed. Browser password prompts for remote SSH are not available on native Windows; configure an SSH key first.
48
64
 
49
65
  ## Install
50
66
 
@@ -69,12 +85,49 @@ codex-partner
69
85
 
70
86
  Open <http://127.0.0.1:8787>.
71
87
 
72
- For access from other devices, set `CODEX_DASHBOARD_HOST=0.0.0.0`. Codex Partner signs browsers in with a username and password accepted by this server's SSH service; the password is verified by OpenSSH and is never stored. Put HTTPS in front of the service before entering an SSH password over an untrusted network.
88
+ Linux keeps SSH browser authentication enabled by default. macOS and Windows default to unauthenticated loopback access because desktop installations normally do not run an SSH server. For access from other devices, set `CODEX_DASHBOARD_HOST=0.0.0.0`, configure `CODEX_DASHBOARD_AUTH=ssh`, and put HTTPS in front of the service. When authentication is handled by a reverse proxy, explicitly set `CODEX_DASHBOARD_AUTH=none`; Codex Partner rejects a non-loopback bind with an implicit unauthenticated default.
89
+
90
+ ### macOS
91
+
92
+ Install Python and Node.js with Homebrew if they are not already available, then install and authenticate Codex:
93
+
94
+ ```bash
95
+ brew install python node
96
+ npm install --global @openai/codex
97
+ codex login
98
+ python3 -m pip install --user --upgrade codex-partner
99
+ python3 -m codex_partner
100
+ ```
101
+
102
+ Open <http://127.0.0.1:8787>. Application data defaults to `~/Library/Application Support/CodexPartner`; native Codex state remains in `~/.codex`.
103
+
104
+ ### Windows
105
+
106
+ Run these commands in PowerShell. Install Python and Node.js first if they are not already available:
107
+
108
+ ```powershell
109
+ winget install --id Python.Python.3.12 -e
110
+ winget install --id OpenJS.NodeJS.LTS -e
111
+ npm install --global @openai/codex
112
+ codex login
113
+ py -m pip install --user --upgrade codex-partner
114
+ py -m codex_partner
115
+ ```
116
+
117
+ Open <http://127.0.0.1:8787>. Application data defaults to `%LOCALAPPDATA%\CodexPartner`; native Codex state remains in `%USERPROFILE%\.codex`. If `codex` is not found after installing Node.js, close and reopen PowerShell so its `PATH` is refreshed.
118
+
119
+ For local desktop use, keep the default `127.0.0.1` bind address. To set options for one PowerShell session:
120
+
121
+ ```powershell
122
+ $env:CODEX_DASHBOARD_PORT = "8787"
123
+ $env:CODEX_DASHBOARD_AUTH = "none"
124
+ py -m codex_partner
125
+ ```
73
126
 
74
127
  ## Docker
75
128
 
76
129
  ```bash
77
- docker build -t codex-partner:0.0.5 .
130
+ docker build -t codex-partner:0.0.7 .
78
131
  docker run -d --name codex-partner \
79
132
  -p 8787:8787 \
80
133
  --add-host=host.docker.internal:host-gateway \
@@ -83,11 +136,11 @@ docker run -d --name codex-partner \
83
136
  -v codex-partner-data:/var/lib/codex-partner \
84
137
  -v codex-home:/home/codex/.codex \
85
138
  -v codex-workspace:/workspace \
86
- codex-partner:0.0.5
139
+ codex-partner:0.0.7
87
140
  docker exec -it codex-partner codex login
88
141
  ```
89
142
 
90
- ## systemd
143
+ ## Linux systemd
91
144
 
92
145
  After completing the source installation above, stop the foreground process and install the user service:
93
146
 
@@ -20,6 +20,19 @@ Codex Partner is a self-hosted control panel for long-running Codex work. It kee
20
20
  - Model and API provider management with health checks and failover
21
21
  - Codex memories, Skills, archive, recycle bin, and SSH workspace support
22
22
  - Default YOLO mode for newly created sessions
23
+ - Safe terminal-to-web takeover: normal web sends ask before stopping a terminal turn; `Alt+Enter` keeps the message queued without interruption
24
+
25
+ ## Platform support
26
+
27
+ Codex Partner runs natively on Linux, macOS, and Windows with Python 3.10 or newer.
28
+
29
+ | Platform | Codex sessions | Workspace tools | Browser terminal | Background service |
30
+ | --- | --- | --- | --- | --- |
31
+ | Linux | Yes | Yes | POSIX PTY | systemd user service |
32
+ | macOS | Yes | Yes | zsh/bash PTY | Run directly or use launchd |
33
+ | Windows 10/11 | Yes | Yes | PowerShell through ConPTY | Run directly or use Task Scheduler |
34
+
35
+ Windows terminal support uses `pywinpty`, which is installed automatically. Key-based remote SSH works when the OpenSSH client is installed. Browser password prompts for remote SSH are not available on native Windows; configure an SSH key first.
23
36
 
24
37
  ## Install
25
38
 
@@ -44,12 +57,49 @@ codex-partner
44
57
 
45
58
  Open <http://127.0.0.1:8787>.
46
59
 
47
- For access from other devices, set `CODEX_DASHBOARD_HOST=0.0.0.0`. Codex Partner signs browsers in with a username and password accepted by this server's SSH service; the password is verified by OpenSSH and is never stored. Put HTTPS in front of the service before entering an SSH password over an untrusted network.
60
+ Linux keeps SSH browser authentication enabled by default. macOS and Windows default to unauthenticated loopback access because desktop installations normally do not run an SSH server. For access from other devices, set `CODEX_DASHBOARD_HOST=0.0.0.0`, configure `CODEX_DASHBOARD_AUTH=ssh`, and put HTTPS in front of the service. When authentication is handled by a reverse proxy, explicitly set `CODEX_DASHBOARD_AUTH=none`; Codex Partner rejects a non-loopback bind with an implicit unauthenticated default.
61
+
62
+ ### macOS
63
+
64
+ Install Python and Node.js with Homebrew if they are not already available, then install and authenticate Codex:
65
+
66
+ ```bash
67
+ brew install python node
68
+ npm install --global @openai/codex
69
+ codex login
70
+ python3 -m pip install --user --upgrade codex-partner
71
+ python3 -m codex_partner
72
+ ```
73
+
74
+ Open <http://127.0.0.1:8787>. Application data defaults to `~/Library/Application Support/CodexPartner`; native Codex state remains in `~/.codex`.
75
+
76
+ ### Windows
77
+
78
+ Run these commands in PowerShell. Install Python and Node.js first if they are not already available:
79
+
80
+ ```powershell
81
+ winget install --id Python.Python.3.12 -e
82
+ winget install --id OpenJS.NodeJS.LTS -e
83
+ npm install --global @openai/codex
84
+ codex login
85
+ py -m pip install --user --upgrade codex-partner
86
+ py -m codex_partner
87
+ ```
88
+
89
+ Open <http://127.0.0.1:8787>. Application data defaults to `%LOCALAPPDATA%\CodexPartner`; native Codex state remains in `%USERPROFILE%\.codex`. If `codex` is not found after installing Node.js, close and reopen PowerShell so its `PATH` is refreshed.
90
+
91
+ For local desktop use, keep the default `127.0.0.1` bind address. To set options for one PowerShell session:
92
+
93
+ ```powershell
94
+ $env:CODEX_DASHBOARD_PORT = "8787"
95
+ $env:CODEX_DASHBOARD_AUTH = "none"
96
+ py -m codex_partner
97
+ ```
48
98
 
49
99
  ## Docker
50
100
 
51
101
  ```bash
52
- docker build -t codex-partner:0.0.5 .
102
+ docker build -t codex-partner:0.0.7 .
53
103
  docker run -d --name codex-partner \
54
104
  -p 8787:8787 \
55
105
  --add-host=host.docker.internal:host-gateway \
@@ -58,11 +108,11 @@ docker run -d --name codex-partner \
58
108
  -v codex-partner-data:/var/lib/codex-partner \
59
109
  -v codex-home:/home/codex/.codex \
60
110
  -v codex-workspace:/workspace \
61
- codex-partner:0.0.5
111
+ codex-partner:0.0.7
62
112
  docker exec -it codex-partner codex login
63
113
  ```
64
114
 
65
- ## systemd
115
+ ## Linux systemd
66
116
 
67
117
  After completing the source installation above, stop the foreground process and install the user service:
68
118
 
@@ -7,7 +7,6 @@ import binascii
7
7
  import codecs
8
8
  import errno
9
9
  import getpass
10
- import fcntl
11
10
  import glob
12
11
  import hashlib
13
12
  import io
@@ -16,7 +15,6 @@ import json
16
15
  import mimetypes
17
16
  import os
18
17
  import posixpath
19
- import pty
20
18
  import platform
21
19
  import re
22
20
  import signal
@@ -27,7 +25,6 @@ import sqlite3
27
25
  import socket
28
26
  import struct
29
27
  import subprocess
30
- import termios
31
28
  import time
32
29
  import uuid
33
30
  import urllib.error
@@ -43,7 +40,20 @@ from fastapi.responses import FileResponse, JSONResponse, StreamingResponse
43
40
  from starlette.middleware.gzip import GZipMiddleware
44
41
  from dotenv import load_dotenv
45
42
  import yaml
46
- import pexpect
43
+ try:
44
+ import fcntl
45
+ import pty
46
+ import termios
47
+ except ImportError: # Native Windows has no POSIX PTY APIs.
48
+ fcntl = pty = termios = None
49
+ try:
50
+ import pexpect
51
+ except ImportError: # Password-driven SSH prompts are optional on Windows.
52
+ pexpect = None
53
+ try:
54
+ from winpty import PtyProcess as WindowsPtyProcess
55
+ except ImportError: # Installed through the Windows-only pywinpty dependency.
56
+ WindowsPtyProcess = None
47
57
  from PIL import Image, ImageOps, ImageSequence, UnidentifiedImageError
48
58
 
49
59
  from codex_partner import APP_NAME, APP_VERSION
@@ -59,6 +69,14 @@ from codex_partner.discovery import (
59
69
  official_candidates,
60
70
  vscode_candidates,
61
71
  )
72
+ from codex_partner.platform_support import (
73
+ IS_WINDOWS,
74
+ default_auth_mode,
75
+ default_data_dir,
76
+ local_shell_command,
77
+ prepare_subprocess_command,
78
+ validate_bind_auth,
79
+ )
62
80
  from codex_partner.schemas import (
63
81
  ApprovalResolveIn,
64
82
  AvatarIn,
@@ -91,12 +109,12 @@ except ModuleNotFoundError: # Python 3.10
91
109
  ROOT = Path(__file__).resolve().parent
92
110
  load_dotenv(ROOT / ".env", override=False)
93
111
  STATIC = ROOT / "static"
94
- DATA_DIR = Path(os.getenv("CODEX_DASHBOARD_DATA", str(ROOT / "data")))
112
+ DATA_DIR = default_data_dir(ROOT)
95
113
  DATA_DIR.mkdir(parents=True, exist_ok=True)
96
114
  DB_PATH = DATA_DIR / "dashboard.sqlite3"
97
115
  CODEX_HOME = Path(os.getenv("CODEX_HOME", str(Path.home() / ".codex"))).expanduser().resolve()
98
116
  CODEX_MEMORY_DIR = CODEX_HOME / "memories"
99
- AUTH_MODE = os.getenv("CODEX_DASHBOARD_AUTH", "ssh").strip().lower()
117
+ AUTH_MODE = os.getenv("CODEX_DASHBOARD_AUTH", default_auth_mode()).strip().lower()
100
118
  if AUTH_MODE not in {"ssh", "none"}:
101
119
  raise RuntimeError("CODEX_DASHBOARD_AUTH must be 'ssh' or 'none'")
102
120
  AUTH_COOKIE = "codex_partner_session"
@@ -128,6 +146,7 @@ def configured_port(name: str, default: int) -> int:
128
146
 
129
147
 
130
148
  DASHBOARD_HOST = os.getenv("CODEX_DASHBOARD_HOST", "127.0.0.1").strip() or "127.0.0.1"
149
+ validate_bind_auth(DASHBOARD_HOST, AUTH_MODE, "CODEX_DASHBOARD_AUTH" in os.environ)
131
150
  DASHBOARD_PORT = configured_port("CODEX_DASHBOARD_PORT", 8787)
132
151
  AUTH_SSH_PORT = configured_port("CODEX_DASHBOARD_AUTH_SSH_PORT", 22)
133
152
  SSH_BIN = shutil.which("ssh") or "ssh"
@@ -135,7 +154,12 @@ SSH_CONFIG = Path.home() / ".ssh/config"
135
154
  SSH_FIXED_HOST = os.getenv("CODEX_DASHBOARD_SSH_HOST", "").strip()
136
155
  SSH_FIXED_USER = os.getenv("CODEX_DASHBOARD_SSH_USER", "").strip()
137
156
  SSH_FIXED_PORT = configured_port("CODEX_DASHBOARD_SSH_PORT", 22)
138
- SSH_RUNTIME_DIR = Path(os.getenv("XDG_RUNTIME_DIR", f"/run/user/{os.getuid()}")) / "codex-dashboard-ssh"
157
+ if os.getenv("XDG_RUNTIME_DIR"):
158
+ SSH_RUNTIME_DIR = Path(os.environ["XDG_RUNTIME_DIR"]) / "codex-dashboard-ssh"
159
+ elif hasattr(os, "getuid") and platform.system() == "Linux":
160
+ SSH_RUNTIME_DIR = Path(f"/run/user/{os.getuid()}") / "codex-dashboard-ssh"
161
+ else:
162
+ SSH_RUNTIME_DIR = DATA_DIR / "ssh-runtime"
139
163
  try:
140
164
  SSH_RUNTIME_DIR.mkdir(parents=True, exist_ok=True, mode=0o700)
141
165
  SSH_RUNTIME_DIR.chmod(0o700)
@@ -207,7 +231,7 @@ def codex_management_snapshot() -> dict[str, Any]:
207
231
  if CODEX_AVAILABLE:
208
232
  try:
209
233
  result = subprocess.run(
210
- [CODEX_BIN, "--version"],
234
+ prepare_subprocess_command([CODEX_BIN, "--version"]),
211
235
  stdin=subprocess.DEVNULL,
212
236
  stdout=subprocess.PIPE,
213
237
  stderr=subprocess.STDOUT,
@@ -1154,8 +1178,23 @@ def rollout_writer_pids(path: str, refresh: bool = False) -> set[int]:
1154
1178
  try:
1155
1179
  processes = os.scandir("/proc")
1156
1180
  except OSError:
1181
+ lsof = shutil.which("lsof")
1182
+ if lsof:
1183
+ try:
1184
+ result = subprocess.run(
1185
+ [lsof, "-t", "--", target],
1186
+ capture_output=True,
1187
+ text=True,
1188
+ encoding="utf-8",
1189
+ errors="replace",
1190
+ timeout=2,
1191
+ check=False,
1192
+ )
1193
+ writers = {int(value) for value in result.stdout.splitlines() if value.isdigit()}
1194
+ except (OSError, subprocess.SubprocessError, ValueError):
1195
+ pass
1157
1196
  rollout_writer_cache[target] = (stamp, writers)
1158
- return writers
1197
+ return set(writers)
1159
1198
  with processes:
1160
1199
  for process in processes:
1161
1200
  if not process.name.isdigit():
@@ -1232,11 +1271,13 @@ def interrupt_rollout_writers(path: str) -> set[int]:
1232
1271
  writers = native_rollout_writer_pids(path, refresh=True)
1233
1272
  for pid in writers:
1234
1273
  try:
1235
- command = Path(f"/proc/{pid}/cmdline").read_bytes().replace(b"\0", b" ")
1236
- if b"app-server" in command:
1237
- continue
1274
+ command_path = Path(f"/proc/{pid}/cmdline")
1275
+ if command_path.is_file():
1276
+ command = command_path.read_bytes().replace(b"\0", b" ")
1277
+ if b"app-server" in command:
1278
+ continue
1238
1279
  os.kill(pid, signal.SIGINT)
1239
- except OSError:
1280
+ except (OSError, ValueError):
1240
1281
  continue
1241
1282
  return writers
1242
1283
 
@@ -1826,7 +1867,7 @@ def ssh_config_aliases(path: Path = SSH_CONFIG, seen: Optional[set[Path]] = None
1826
1867
  return []
1827
1868
  for line in lines:
1828
1869
  try:
1829
- parts = shlex.split(line, comments=True, posix=True)
1870
+ parts = shlex.split(line, comments=True, posix=not IS_WINDOWS)
1830
1871
  except ValueError:
1831
1872
  continue
1832
1873
  if not parts:
@@ -1995,6 +2036,8 @@ exit 1
1995
2036
 
1996
2037
 
1997
2038
  def password_ssh_connect(host: str, password: str) -> tuple[bool, str]:
2039
+ if pexpect is None:
2040
+ return False, "SSH password authentication is unavailable on this platform"
1998
2041
  args = [
1999
2042
  *ssh_options(host, batch=False),
2000
2043
  "-o", "ConnectTimeout=10",
@@ -3613,7 +3656,7 @@ def appserver_turn_inputs(task: dict, message: str) -> list[dict[str, Any]]:
3613
3656
  continue
3614
3657
  seen.add((kind, path))
3615
3658
  if kind == "mention":
3616
- inputs.append({"type": "mention", "name": PurePosixPath(path).name, "path": path})
3659
+ inputs.append({"type": "mention", "name": Path(path).name, "path": path})
3617
3660
  else:
3618
3661
  item: dict[str, Any] = {"type": kind, "path": path}
3619
3662
  if kind == "localImage":
@@ -3867,6 +3910,13 @@ async def close_terminal_session(terminal_id: str) -> None:
3867
3910
  if not session:
3868
3911
  return
3869
3912
  session["closed"] = True
3913
+ windows_pty = session.get("windows_pty")
3914
+ if windows_pty is not None:
3915
+ try:
3916
+ await asyncio.to_thread(lambda: windows_pty.close(force=True))
3917
+ except Exception:
3918
+ pass
3919
+ return
3870
3920
  fd, pid = session.get("fd"), session.get("pid")
3871
3921
  loop = session.get("loop")
3872
3922
  if loop and fd is not None:
@@ -3905,6 +3955,81 @@ def set_terminal_size(fd: int, cols: int, rows: int) -> None:
3905
3955
  fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", rows, cols, 0, 0))
3906
3956
 
3907
3957
 
3958
+ async def windows_terminal_socket(websocket: WebSocket, task_id: str, cwd: str, ssh_host: str) -> None:
3959
+ if WindowsPtyProcess is None:
3960
+ await websocket.send_json({"type": "error", "message": "Windows terminal support requires pywinpty"})
3961
+ await websocket.close(code=1011)
3962
+ return
3963
+ env = os.environ.copy()
3964
+ env.update({"TERM": "xterm-256color", "COLORTERM": "truecolor"})
3965
+ if ssh_host:
3966
+ remote_command = f"cd -- {shlex.quote(cwd)} && exec \"${{SHELL:-/bin/sh}}\" -il"
3967
+ command = [*ssh_options(ssh_host, batch=True, tty=True), ssh_destination(ssh_host), remote_command]
3968
+ shell_label = f"ssh:{ssh_host}"
3969
+ else:
3970
+ command, shell_label = local_shell_command()
3971
+ try:
3972
+ spawn_cwd = cwd if not ssh_host else str(DEFAULT_WORKSPACE)
3973
+ process = await asyncio.to_thread(WindowsPtyProcess.spawn, command, cwd=spawn_cwd, env=env)
3974
+ except Exception as exc:
3975
+ await websocket.send_json({"type": "error", "message": f"terminal start failed: {exc}"})
3976
+ await websocket.close(code=1011)
3977
+ return
3978
+
3979
+ terminal_id = str(uuid.uuid4())
3980
+ session = {"windows_pty": process, "task_id": task_id, "closed": False}
3981
+ terminal_sessions[terminal_id] = session
3982
+ await websocket.send_json({"type": "ready", "terminal_id": terminal_id, "cwd": cwd, "shell": shell_label})
3983
+
3984
+ async def pump_output() -> None:
3985
+ while not session["closed"]:
3986
+ try:
3987
+ data = await asyncio.to_thread(process.read, 65536)
3988
+ except (EOFError, OSError):
3989
+ data = ""
3990
+ if data:
3991
+ await websocket.send_json({"type": "output", "data": data})
3992
+ continue
3993
+ await websocket.send_json({"type": "exit", "code": getattr(process, "exitstatus", None)})
3994
+ return
3995
+
3996
+ async def receive_input() -> None:
3997
+ while True:
3998
+ incoming = await websocket.receive_json()
3999
+ kind = incoming.get("type")
4000
+ if kind == "input":
4001
+ value = incoming.get("data", incoming.get("input", ""))
4002
+ if isinstance(value, str) and value:
4003
+ await asyncio.to_thread(process.write, value)
4004
+ elif kind == "resize":
4005
+ try:
4006
+ cols = max(20, min(int(incoming.get("cols", 120)), 400))
4007
+ rows = max(4, min(int(incoming.get("rows", 32)), 160))
4008
+ await asyncio.to_thread(process.setwinsize, rows, cols)
4009
+ except (OSError, ValueError, TypeError):
4010
+ continue
4011
+ elif kind == "ping":
4012
+ await websocket.send_json({"type": "pong"})
4013
+ elif kind == "close":
4014
+ return
4015
+
4016
+ pump_task = asyncio.create_task(pump_output())
4017
+ receive_task = asyncio.create_task(receive_input())
4018
+ try:
4019
+ _done, pending = await asyncio.wait({pump_task, receive_task}, return_when=asyncio.FIRST_COMPLETED)
4020
+ for waiter in pending:
4021
+ waiter.cancel()
4022
+ await asyncio.gather(*pending, return_exceptions=True)
4023
+ except WebSocketDisconnect:
4024
+ pass
4025
+ finally:
4026
+ for waiter in (pump_task, receive_task):
4027
+ if not waiter.done():
4028
+ waiter.cancel()
4029
+ await asyncio.gather(pump_task, receive_task, return_exceptions=True)
4030
+ await close_terminal_session(terminal_id)
4031
+
4032
+
3908
4033
  @app.websocket("/ws/terminal/{task_id}")
3909
4034
  async def terminal_socket(websocket: WebSocket, task_id: str):
3910
4035
  if not websocket_auth_session(websocket):
@@ -3921,11 +4046,17 @@ async def terminal_socket(websocket: WebSocket, task_id: str):
3921
4046
  await websocket.close(code=4428, reason="SSH login required")
3922
4047
  return
3923
4048
  await websocket.accept()
3924
- terminal_id = str(uuid.uuid4())
3925
4049
  cwd = task["workspace"] if ssh_host else (task["workspace"] if Path(task["workspace"]).is_dir() else str(DEFAULT_WORKSPACE))
3926
- shell = f"ssh:{ssh_host}" if ssh_host else os.environ.get("SHELL", "/bin/bash")
3927
- if not ssh_host and not Path(shell).is_file():
3928
- shell = "/bin/bash"
4050
+ if IS_WINDOWS:
4051
+ await windows_terminal_socket(websocket, task_id, cwd, ssh_host)
4052
+ return
4053
+ terminal_id = str(uuid.uuid4())
4054
+ shell_command, local_shell = local_shell_command()
4055
+ shell = f"ssh:{ssh_host}" if ssh_host else local_shell
4056
+ if pty is None or fcntl is None or termios is None:
4057
+ await websocket.send_json({"type": "error", "message": "PTY support is unavailable on this platform"})
4058
+ await websocket.close(code=1011)
4059
+ return
3929
4060
  pid, fd = pty.fork()
3930
4061
  if pid == 0:
3931
4062
  try:
@@ -3937,7 +4068,7 @@ async def terminal_socket(websocket: WebSocket, task_id: str):
3937
4068
  os.execvpe(command[0], command, env)
3938
4069
  else:
3939
4070
  os.chdir(cwd)
3940
- os.execvpe(shell, [shell, "-i"], env)
4071
+ os.execvpe(shell_command[0], shell_command, env)
3941
4072
  except Exception as exc:
3942
4073
  os.write(2, f"terminal start failed: {exc}\n".encode())
3943
4074
  os._exit(127)
@@ -4176,6 +4307,61 @@ async def enqueue_task_message(task_id: str, body: str, client_message_id: Optio
4176
4307
  return db.one("SELECT * FROM task_messages WHERE id=?", (message_id,)) or {"id": message_id, "status": "queued"}
4177
4308
 
4178
4309
 
4310
+ async def terminal_turn_active(task_id: str) -> bool:
4311
+ """Refresh and report a terminal-owned turn without confusing dashboard work."""
4312
+ await refresh_live_external_turn(task_id)
4313
+ return bool(refresh_external_primary(task_id)) and not dashboard_owns_task(task_id)
4314
+
4315
+
4316
+ async def interrupt_browser_terminal(task_id: str) -> None:
4317
+ """Send Ctrl+C to the foreground process in every matching browser terminal."""
4318
+ matching = [session for session in terminal_sessions.values() if session.get("task_id") == task_id and not session.get("closed")]
4319
+ for session in matching:
4320
+ try:
4321
+ if process := session.get("windows_pty"):
4322
+ await asyncio.to_thread(process.write, "\x03")
4323
+ elif session.get("fd") is not None:
4324
+ os.write(session["fd"], b"\x03")
4325
+ except (EOFError, OSError):
4326
+ continue
4327
+
4328
+
4329
+ async def stop_terminal_for_web_takeover(task_id: str) -> bool:
4330
+ """Stop terminal-owned turns without letting Goal auto-resume race the web message."""
4331
+ await refresh_live_external_turn(task_id)
4332
+ tracked = list((external_turn_sets.get(task_id) or {}).values())
4333
+ if not tracked:
4334
+ return False
4335
+ await interrupt_browser_terminal(task_id)
4336
+ paths = {str(turn.get("path") or "") for turn in tracked} - {""}
4337
+ for path in paths:
4338
+ await asyncio.to_thread(interrupt_rollout_writers, path)
4339
+ clear_external_turns(task_id)
4340
+ stamp = now()
4341
+ for external in tracked:
4342
+ session_id = str(external.get("session_id") or "")
4343
+ if not session_id:
4344
+ continue
4345
+ db.execute(
4346
+ "UPDATE sessions SET status='stopped',finished_at=COALESCE(finished_at,?),exit_code=COALESCE(exit_code,130),summary='Terminal turn stopped for web takeover' WHERE id=?",
4347
+ (stamp, session_id),
4348
+ )
4349
+ payload = {"type": "turn_aborted", "status": "stopped", "reason": "Web session took over the terminal turn"}
4350
+ db.execute(
4351
+ "INSERT INTO events (session_id,ts,stream,payload) VALUES (?,?,?,?)",
4352
+ (session_id, stamp, "rollout", json.dumps(payload, ensure_ascii=False)),
4353
+ )
4354
+ await broadcast_task(task_id, {"type": "event", "session_id": session_id, "stream": "rollout", "payload": payload, "ts": stamp})
4355
+ db.execute(
4356
+ "UPDATE tasks SET status='stopped',active_session_id=NULL,execution_source='',execution_turn_id='',run_mode='',last_error='',updated_at=? WHERE id=?",
4357
+ (stamp, task_id),
4358
+ )
4359
+ result = task_or_404(task_id)
4360
+ await broadcast_task(task_id, {"type": "task_status", "task": result, "source": {"kind": "web_takeover"}})
4361
+ await broadcast_overview(task_id, {"kind": "web_takeover"})
4362
+ return True
4363
+
4364
+
4179
4365
  def command_event_session(task: dict) -> str:
4180
4366
  if task.get("active_session_id") and db.one("SELECT id FROM sessions WHERE id=?", (task["active_session_id"],)):
4181
4367
  return task["active_session_id"]
@@ -4473,7 +4659,7 @@ async def install_codex(force: bool = False, _: Any = Depends(auth)):
4473
4659
  env["PATH"] = os.pathsep.join((str(Path(npm).parent), str(extra_bin), env.get("PATH", "")))
4474
4660
  try:
4475
4661
  process = await asyncio.create_subprocess_exec(
4476
- *command,
4662
+ *prepare_subprocess_command(command),
4477
4663
  stdout=asyncio.subprocess.PIPE,
4478
4664
  stderr=asyncio.subprocess.STDOUT,
4479
4665
  env=env,
@@ -5655,6 +5841,8 @@ async def supervise(task: dict, session_id: str, providers: list[Optional[dict]]
5655
5841
  cwd = None
5656
5842
  env.pop("OPENAI_API_KEY", None)
5657
5843
  env.pop("OPENAI_BASE_URL", None)
5844
+ else:
5845
+ spawn_cmd = prepare_subprocess_command(spawn_cmd)
5658
5846
  process = await asyncio.create_subprocess_exec(*spawn_cmd, cwd=cwd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, env=env)
5659
5847
  async with running_lock:
5660
5848
  running[task_id] = process
@@ -5802,6 +5990,16 @@ async def clear_task_messages(task_id: str, _: Any = Depends(auth)):
5802
5990
 
5803
5991
  @app.post("/api/tasks/{task_id}/messages")
5804
5992
  async def post_task_message(task_id: str, payload: TaskMessageIn, _: Any = Depends(auth)):
5993
+ if payload.delivery != "queue" and await terminal_turn_active(task_id):
5994
+ if not payload.takeover_terminal:
5995
+ raise HTTPException(
5996
+ 409,
5997
+ detail={
5998
+ "code": "terminal_turn_active",
5999
+ "message": "Codex 正在终端中运行。是否停止终端 turn 并由网页接管?",
6000
+ },
6001
+ )
6002
+ await stop_terminal_for_web_takeover(task_id)
5805
6003
  return await enqueue_task_message(task_id, payload.message, payload.client_message_id, payload.delivery)
5806
6004
 
5807
6005
 
@@ -6249,7 +6447,7 @@ def git_diff_summary(task: dict) -> str:
6249
6447
  sections = []
6250
6448
  for index, command in enumerate(commands):
6251
6449
  try:
6252
- completed = ssh_capture(task["ssh_host"], shlex.join(command), 12) if task.get("ssh_host") else subprocess.run(command, capture_output=True, text=True, timeout=8, check=False)
6450
+ completed = ssh_capture(task["ssh_host"], shlex.join(command), 12) if task.get("ssh_host") else subprocess.run(prepare_subprocess_command(command), capture_output=True, text=True, timeout=8, check=False)
6253
6451
  except (OSError, subprocess.TimeoutExpired) as exc:
6254
6452
  return f"无法读取 Git 变更:{exc}"
6255
6453
  output = (completed.stdout or completed.stderr).strip()
@@ -6805,6 +7003,8 @@ async def supervise_operation(task: dict, session_id: str, cmd: list[str]) -> No
6805
7003
  connection = await require_ssh_connection(task["ssh_host"], codex=True)
6806
7004
  spawn_cmd = [*ssh_options(task["ssh_host"], batch=True), ssh_destination(task["ssh_host"]), shlex.join([connection["codex_bin"], *cmd[1:]])]
6807
7005
  cwd = None
7006
+ else:
7007
+ spawn_cmd = prepare_subprocess_command(spawn_cmd)
6808
7008
  process = await asyncio.create_subprocess_exec(*spawn_cmd, cwd=cwd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, env=os.environ.copy())
6809
7009
  async with running_lock:
6810
7010
  running[task_id] = process
@@ -1,4 +1,4 @@
1
1
  """Codex Partner application package."""
2
2
 
3
3
  APP_NAME = "Codex Partner"
4
- APP_VERSION = "0.0.5"
4
+ APP_VERSION = "0.0.7"
@@ -4,6 +4,8 @@ import asyncio
4
4
  import json
5
5
  from typing import Any, Awaitable, Callable, Optional
6
6
 
7
+ from .platform_support import prepare_subprocess_command
8
+
7
9
 
8
10
  NotificationHandler = Callable[[str, dict], Awaitable[None]]
9
11
 
@@ -55,8 +57,9 @@ class AppServerClient:
55
57
  await asyncio.wait_for(self.process.wait(), 1)
56
58
  except Exception:
57
59
  pass
60
+ command = prepare_subprocess_command(self.command)
58
61
  self.process = await asyncio.create_subprocess_exec(
59
- *self.command,
62
+ *command,
60
63
  stdin=asyncio.subprocess.PIPE,
61
64
  stdout=asyncio.subprocess.PIPE,
62
65
  stderr=asyncio.subprocess.DEVNULL,
@@ -2,6 +2,7 @@
2
2
 
3
3
  import sqlite3
4
4
  import threading
5
+ from contextlib import closing
5
6
  from pathlib import Path
6
7
  from typing import Callable, Optional
7
8
 
@@ -27,7 +28,7 @@ class Database:
27
28
  return connection
28
29
 
29
30
  def initialize(self) -> None:
30
- with self.lock, self.connect() as connection:
31
+ with self.lock, closing(self.connect()) as connection:
31
32
  connection.executescript(
32
33
  """
33
34
  CREATE TABLE IF NOT EXISTS tasks (
@@ -262,15 +263,15 @@ class Database:
262
263
  )
263
264
 
264
265
  def one(self, sql: str, args: tuple = ()) -> Optional[dict]:
265
- with self.lock, self.connect() as connection:
266
+ with self.lock, closing(self.connect()) as connection:
266
267
  row = connection.execute(sql, args).fetchone()
267
268
  return dict(row) if row else None
268
269
 
269
270
  def all(self, sql: str, args: tuple = ()) -> list[dict]:
270
- with self.lock, self.connect() as connection:
271
+ with self.lock, closing(self.connect()) as connection:
271
272
  return [dict(row) for row in connection.execute(sql, args).fetchall()]
272
273
 
273
274
  def execute(self, sql: str, args: tuple = ()) -> None:
274
- with self.lock, self.connect() as connection:
275
+ with self.lock, closing(self.connect()) as connection:
275
276
  connection.execute(sql, args)
276
277
  connection.commit()