abstra 3.23.12__py3-none-any.whl → 3.24.0__py3-none-any.whl

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 (262) hide show
  1. abstra/cli.py +8 -3
  2. {abstra-3.23.12.dist-info → abstra-3.24.0.dist-info}/METADATA +2 -1
  3. {abstra-3.23.12.dist-info → abstra-3.24.0.dist-info}/RECORD +197 -187
  4. abstra_internals/consts/filepaths.py +1 -1
  5. abstra_internals/contracts_generated.py +665 -655
  6. abstra_internals/controllers/git.py +305 -0
  7. abstra_internals/controllers/main.py +10 -6
  8. abstra_internals/environment.py +6 -0
  9. abstra_internals/interface/cli/deploy.py +1 -1
  10. abstra_internals/interface/sdk/ai.py +1 -0
  11. abstra_internals/repositories/git/__init__.py +38 -0
  12. abstra_internals/repositories/git/dulwich.py +1353 -0
  13. abstra_internals/repositories/git/git_test.py +1572 -0
  14. abstra_internals/repositories/git/native.py +578 -0
  15. abstra_internals/repositories/git/types.py +267 -0
  16. abstra_internals/repositories/linter/rules/env_in_bundle.py +5 -5
  17. abstra_internals/repositories/linter/rules/env_in_bundle_test.py +6 -6
  18. abstra_internals/repositories/linter/rules/venv_in_bundle.py +9 -16
  19. abstra_internals/server/blueprints/editor.py +4 -0
  20. abstra_internals/server/routes/git.py +190 -0
  21. abstra_internals/server/routes/workspace.py +1 -1
  22. abstra_internals/services/file_watcher.py +32 -13
  23. abstra_internals/services/fs.py +4 -4
  24. abstra_internals/services/fs_test.py +4 -6
  25. abstra_internals/templates/__init__.py +0 -11
  26. abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.eb8ccb64.js +2 -0
  27. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.9f98292e.js +2 -0
  28. abstra_statics/dist/assets/ApiKeys.e975c4f3.js +2 -0
  29. abstra_statics/dist/assets/App.f62faff6.js +2 -0
  30. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.5aa45ac1.js +2 -0
  31. abstra_statics/dist/assets/BaseLayout.1ec2c96d.js +2 -0
  32. abstra_statics/dist/assets/{Billing.461db3ef.js → Billing.60adc9fa.js} +2 -2
  33. abstra_statics/dist/assets/{Breadcrumb.27110ec4.js → Breadcrumb.26dec5f7.js} +2 -2
  34. abstra_statics/dist/assets/{Builds.4eecd717.js → Builds.f86210bc.js} +2 -2
  35. abstra_statics/dist/assets/{Card.1d1a9fb7.js → Card.6cfffb80.js} +2 -2
  36. abstra_statics/dist/assets/{CircularLoading.fc66331b.js → CircularLoading.a13d6a76.js} +2 -2
  37. abstra_statics/dist/assets/{CloseCircleOutlined.0110bbe2.js → CloseCircleOutlined.30bc25a8.js} +2 -2
  38. abstra_statics/dist/assets/{ConnectorsView.82e74ae4.css → ConnectorsView.33c5380f.css} +1 -1
  39. abstra_statics/dist/assets/ConnectorsView.eb4c769f.js +2 -0
  40. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.8d2e4672.js +2 -0
  41. abstra_statics/dist/assets/ContentLayout.d03fee5b.js +2 -0
  42. abstra_statics/dist/assets/{CrudView.c16e2f81.js → CrudView.c0824225.js} +2 -2
  43. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.3668aee4.js +2 -0
  44. abstra_statics/dist/assets/{EditorLogin.00a3b3d9.js → EditorLogin.46db248f.js} +2 -2
  45. abstra_statics/dist/assets/{EditorsView.f7b2843c.js → EditorsView.e72621fa.js} +2 -2
  46. abstra_statics/dist/assets/EnvVars.f9f9d61f.js +2 -0
  47. abstra_statics/dist/assets/{Error.466dbb94.js → Error.864f05b3.js} +2 -2
  48. abstra_statics/dist/assets/ExclamationCircleOutlined.a489b996.js +2 -0
  49. abstra_statics/dist/assets/{Files.104385dd.js → Files.afe615e1.js} +2 -2
  50. abstra_statics/dist/assets/{Form.8077681f.js → Form.556d0de2.js} +2 -2
  51. abstra_statics/dist/assets/{FormRunner.055e2c45.js → FormRunner.7f56a8c6.js} +2 -2
  52. abstra_statics/dist/assets/{Home.0ef22910.js → Home.287d17f8.js} +2 -2
  53. abstra_statics/dist/assets/Home.5b7e9c23.js +2 -0
  54. abstra_statics/dist/assets/{Live.e1261458.js → Live.50bacfea.js} +2 -2
  55. abstra_statics/dist/assets/LoadingContainer.ebace8de.js +2 -0
  56. abstra_statics/dist/assets/{LoadingOutlined.cd84d9c9.js → LoadingOutlined.9e949112.js} +2 -2
  57. abstra_statics/dist/assets/{Login.041361ea.js → Login.536a3067.js} +2 -2
  58. abstra_statics/dist/assets/Login.bec408c9.js +2 -0
  59. abstra_statics/dist/assets/{Login.vue_vue_type_script_setup_true_lang.c2eb444c.js → Login.vue_vue_type_script_setup_true_lang.1c3f108d.js} +2 -2
  60. abstra_statics/dist/assets/Logo.82d6ab70.js +2 -0
  61. abstra_statics/dist/assets/{Logs.f76cde12.js → Logs.f6135084.js} +2 -2
  62. abstra_statics/dist/assets/{LogsController.addd81bf.js → LogsController.6b666816.js} +2 -2
  63. abstra_statics/dist/assets/Main.77b115f8.js +2 -0
  64. abstra_statics/dist/assets/{MockForm.c9441864.js → MockForm.deda9355.js} +2 -2
  65. abstra_statics/dist/assets/{Navbar.07ba9452.js → Navbar.4a6f2b09.js} +2 -2
  66. abstra_statics/dist/assets/NewEditor.5f84de86.css +1 -0
  67. abstra_statics/dist/assets/NewEditor.e558e47d.js +8 -0
  68. abstra_statics/dist/assets/OidcLoginCallback.7f514b45.js +2 -0
  69. abstra_statics/dist/assets/OidcLogoutCallback.038813a1.js +2 -0
  70. abstra_statics/dist/assets/OmniChat.05ba8d8a.css +1 -0
  71. abstra_statics/dist/assets/OmniChat.60d98deb.js +6 -0
  72. abstra_statics/dist/assets/{OnboardingView.fbc4b6fe.js → OnboardingView.9413ee50.js} +2 -2
  73. abstra_statics/dist/assets/{Organization.bc495099.js → Organization.7203cc0b.js} +2 -2
  74. abstra_statics/dist/assets/{Organizations.b630803f.js → Organizations.91220ca0.js} +2 -2
  75. abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.156bcd89.js → PhArrowCounterClockwise.vue.8090d021.js} +2 -2
  76. abstra_statics/dist/assets/{PhArrowSquareOut.vue.d0c95a06.js → PhArrowSquareOut.vue.26582195.js} +2 -2
  77. abstra_statics/dist/assets/{PhBookBookmark.vue.42e49494.js → PhBookBookmark.vue.5b7ab079.js} +2 -2
  78. abstra_statics/dist/assets/{PhChats.vue.54d692e4.js → PhChats.vue.b5df7174.js} +2 -2
  79. abstra_statics/dist/assets/{PhClockCounterClockwise.vue.d47d66ba.js → PhClockCounterClockwise.vue.812311ad.js} +2 -2
  80. abstra_statics/dist/assets/{PhCopy.vue.a0d9b0ec.js → PhCopy.vue.59b0f1b4.js} +2 -2
  81. abstra_statics/dist/assets/PhCopySimple.vue.d41d9160.js +2 -0
  82. abstra_statics/dist/assets/{PhCube.vue.498c014d.js → PhCube.vue.63ae7d32.js} +2 -2
  83. abstra_statics/dist/assets/PhDatabase.vue.edfcb96b.js +2 -0
  84. abstra_statics/dist/assets/{PhDotsThreeVertical.vue.0ea03d82.js → PhDotsThreeVertical.vue.ab4580a5.js} +2 -2
  85. abstra_statics/dist/assets/PhDownloadSimple.vue.c2eaaad1.js +2 -0
  86. abstra_statics/dist/assets/{PhFolderPlus.vue.0c210f8d.js → PhFolderPlus.vue.05ba4a5c.js} +2 -2
  87. abstra_statics/dist/assets/{PhGear.vue.86c3014a.js → PhGear.vue.0e4a6135.js} +2 -2
  88. abstra_statics/dist/assets/{PhKey.vue.72ce23d3.js → PhKey.vue.b2c184d1.js} +2 -2
  89. abstra_statics/dist/assets/{PhPencil.vue.80ed4b2e.js → PhPencil.vue.2f2fe576.js} +2 -2
  90. abstra_statics/dist/assets/{PhPencilSimple.vue.0046d784.js → PhPencilSimple.vue.cc8620ae.js} +2 -2
  91. abstra_statics/dist/assets/{PhRocket.vue.f3302a7e.js → PhRocket.vue.e397203c.js} +2 -2
  92. abstra_statics/dist/assets/{PhSignOut.vue.c9150da4.js → PhSignOut.vue.a271f14b.js} +2 -2
  93. abstra_statics/dist/assets/{PhSparkle.vue.84ea95fc.js → PhSparkle.vue.726defa2.js} +2 -2
  94. abstra_statics/dist/assets/{PhUserList.vue.bce47902.js → PhUserList.vue.6a29f16d.js} +2 -2
  95. abstra_statics/dist/assets/{PhUsersThree.vue.0a84dfa4.js → PhUsersThree.vue.275d13ab.js} +2 -2
  96. abstra_statics/dist/assets/PhWarningCircle.vue.3b1aca1b.js +2 -0
  97. abstra_statics/dist/assets/{PhWebhooksLogo.vue.a704632d.js → PhWebhooksLogo.vue.5e772aac.js} +2 -2
  98. abstra_statics/dist/assets/{PlayerConfigProvider.26b585a1.js → PlayerConfigProvider.e90a2b41.js} +2 -2
  99. abstra_statics/dist/assets/{PlayerNavbar.0fa6f760.js → PlayerNavbar.11ec1844.js} +2 -2
  100. abstra_statics/dist/assets/Project.c16740fb.js +2 -0
  101. abstra_statics/dist/assets/ProjectLogin.e7a6f444.js +2 -0
  102. abstra_statics/dist/assets/{ProjectSettings.2bf7e6c8.js → ProjectSettings.52c19693.js} +2 -2
  103. abstra_statics/dist/assets/{ProjectsView.ed31b921.js → ProjectsView.22fa7a8e.js} +2 -2
  104. abstra_statics/dist/assets/{SaveButton.fdf70b31.js → SaveButton.719393d2.js} +2 -2
  105. abstra_statics/dist/assets/{ScrollArea.vue_vue_type_script_setup_true_lang.3bbea426.js → ScrollArea.vue_vue_type_script_setup_true_lang.d4028954.js} +2 -2
  106. abstra_statics/dist/assets/{Sidebar.e69f49bd.css → Sidebar.29baeab0.css} +1 -1
  107. abstra_statics/dist/assets/{Sidebar.781afa0a.js → Sidebar.5cb8e04e.js} +2 -2
  108. abstra_statics/dist/assets/Sql.23d80bad.js +5 -0
  109. abstra_statics/dist/assets/Sql.90e6e2ba.css +1 -0
  110. abstra_statics/dist/assets/Steps.8e5d201a.js +2 -0
  111. abstra_statics/dist/assets/TableCard.529112b9.css +1 -0
  112. abstra_statics/dist/assets/TableCard.59f95f8f.js +2 -0
  113. abstra_statics/dist/assets/TableEditor.5853a363.css +1 -0
  114. abstra_statics/dist/assets/TableEditor.8539f984.js +2 -0
  115. abstra_statics/dist/assets/Tables.44d953f7.js +2 -0
  116. abstra_statics/dist/assets/TablesDiagram.8e47383c.js +15 -0
  117. abstra_statics/dist/assets/TablesDiagram.a588e7ff.css +1 -0
  118. abstra_statics/dist/assets/{TablesTabs.vue_vue_type_script_setup_true_lang.63aa07d0.js → TablesTabs.vue_vue_type_script_setup_true_lang.6866fb32.js} +2 -2
  119. abstra_statics/dist/assets/{Tasks.ee450480.js → Tasks.09551b19.js} +2 -2
  120. abstra_statics/dist/assets/{UploadOutlined.e3072945.js → UploadOutlined.eab75eb0.js} +2 -2
  121. abstra_statics/dist/assets/View.5fd7ddf0.js +2 -0
  122. abstra_statics/dist/assets/{View.vue_vue_type_script_setup_true_lang.ee5d447b.js → View.vue_vue_type_script_setup_true_lang.a904f400.js} +2 -2
  123. abstra_statics/dist/assets/{Watermark.891eee9f.js → Watermark.ab3d818f.js} +2 -2
  124. abstra_statics/dist/assets/{WebEditor.7409cd48.js → WebEditor.d6ec6392.js} +2 -2
  125. abstra_statics/dist/assets/WidgetPreview.86b31dec.js +2 -0
  126. abstra_statics/dist/assets/ant-design.a865486e.js +2 -0
  127. abstra_statics/dist/assets/apiKey.72f497ca.js +2 -0
  128. abstra_statics/dist/assets/asyncComputed.cf5282fc.js +2 -0
  129. abstra_statics/dist/assets/{build.8fa1a961.js → build.df2d55cc.js} +2 -2
  130. abstra_statics/dist/assets/{colorHelpers.71d6d61d.js → colorHelpers.37d9932b.js} +2 -2
  131. abstra_statics/dist/assets/console.2bf7f04d.js +17 -0
  132. abstra_statics/dist/assets/constants.7d38ec8b.js +2 -0
  133. abstra_statics/dist/assets/{contracts.generated.c4057ed0.js → contracts.generated.590b1102.js} +2 -2
  134. abstra_statics/dist/assets/{cssMode.4c65b876.js → cssMode.7133c7cb.js} +2 -2
  135. abstra_statics/dist/assets/datetime.8de2ff28.js +2 -0
  136. abstra_statics/dist/assets/dayjs.f18bbbca.js +2 -0
  137. abstra_statics/dist/assets/editor.3a4714e3.js +2 -0
  138. abstra_statics/dist/assets/editor.main.9c635b9a.js +2 -0
  139. abstra_statics/dist/assets/fetch.89fd5b7b.js +2 -0
  140. abstra_statics/dist/assets/folder.d8e23009.js +2 -0
  141. abstra_statics/dist/assets/{freemarker2.1d872d48.js → freemarker2.6698d1ea.js} +2 -2
  142. abstra_statics/dist/assets/{handlebars.41fc6db8.js → handlebars.a6c42dc0.js} +2 -2
  143. abstra_statics/dist/assets/{html.967e3c6d.js → html.493a5410.js} +3 -3
  144. abstra_statics/dist/assets/{htmlMode.d38ab72a.js → htmlMode.a28b2fca.js} +2 -2
  145. abstra_statics/dist/assets/{index.51dbb698.js → index.2af3391c.js} +2 -2
  146. abstra_statics/dist/assets/{index.9021ba5d.js → index.4176fe88.js} +2 -2
  147. abstra_statics/dist/assets/{index.b762f5e8.js → index.4d20c159.js} +2 -2
  148. abstra_statics/dist/assets/{index.8e871bae.js → index.5d6b1e62.js} +2 -2
  149. abstra_statics/dist/assets/index.79ce3bf1.js +2 -0
  150. abstra_statics/dist/assets/{index.03d222dd.js → index.c34a405a.js} +2 -2
  151. abstra_statics/dist/assets/index.fb182bd1.js +2 -0
  152. abstra_statics/dist/assets/{index.5f3f38ed.js → index.fb49354b.js} +2 -2
  153. abstra_statics/dist/assets/{javascript.0935bea2.js → javascript.3a36cf17.js} +3 -3
  154. abstra_statics/dist/assets/{jsonMode.2860b71c.js → jsonMode.bead6ac8.js} +2 -2
  155. abstra_statics/dist/assets/{jwt-decode.esm.7f2ef0df.js → jwt-decode.esm.d4517a10.js} +8 -8
  156. abstra_statics/dist/assets/linters.2f3141cb.js +2 -0
  157. abstra_statics/dist/assets/{liquid.1bae5f6b.js → liquid.0c337fae.js} +3 -3
  158. abstra_statics/dist/assets/{member.b7ac8000.js → member.48d6f2cd.js} +2 -2
  159. abstra_statics/dist/assets/{metadata.39f9b9ba.js → metadata.c3aed6e1.js} +2 -2
  160. abstra_statics/dist/assets/{omniChatStore.3431c026.js → omniChatStore.c53bcca2.js} +3 -3
  161. abstra_statics/dist/assets/{organization.a877b653.js → organization.928c9bef.js} +2 -2
  162. abstra_statics/dist/assets/player.d3aeafc5.js +2 -0
  163. abstra_statics/dist/assets/{plotly.min.d3f75723.js → plotly.min.7225d3a0.js} +2 -2
  164. abstra_statics/dist/assets/polling.82ee6b45.js +2 -0
  165. abstra_statics/dist/assets/{project.d22a89ee.js → project.619b7244.js} +2 -2
  166. abstra_statics/dist/assets/{python.4c1a1300.js → python.05764499.js} +3 -3
  167. abstra_statics/dist/assets/{razor.ba8bdb33.js → razor.81a45581.js} +3 -3
  168. abstra_statics/dist/assets/record.7f43486c.js +2 -0
  169. abstra_statics/dist/assets/{redirect.ce3c0f65.js → redirect.f028a879.js} +2 -2
  170. abstra_statics/dist/assets/repository.9534db4b.js +2 -0
  171. abstra_statics/dist/assets/repository.c15239ce.js +2 -0
  172. abstra_statics/dist/assets/router.262190ec.js +2 -0
  173. abstra_statics/dist/assets/router.424f7da9.js +18 -0
  174. abstra_statics/dist/assets/{string.2ed1cde3.js → string.0acf5572.js} +2 -2
  175. abstra_statics/dist/assets/{tables.be1c51f8.js → tables.1f68ec62.js} +2 -2
  176. abstra_statics/dist/assets/{tasksController.bf12e264.js → tasksController.371896de.js} +2 -2
  177. abstra_statics/dist/assets/{toggleHighContrast.c4e1b24d.js → toggleHighContrast.0d0e5662.js} +7 -7
  178. abstra_statics/dist/assets/{tsMode.da264aae.js → tsMode.6eadbf06.js} +2 -2
  179. abstra_statics/dist/assets/{typescript.db5d7517.js → typescript.1670e287.js} +3 -3
  180. abstra_statics/dist/assets/url.e8732f77.js +2 -0
  181. abstra_statics/dist/assets/{useCodebaseEvents.42573b00.js → useCodebaseEvents.53dec1f2.js} +2 -2
  182. abstra_statics/dist/assets/useTables.4f034cf8.js +2 -0
  183. abstra_statics/dist/assets/userStore.31024da3.js +2 -0
  184. abstra_statics/dist/assets/uuid.bde15ce7.js +2 -0
  185. abstra_statics/dist/assets/vue-flow-background.818c7852.js +2 -0
  186. abstra_statics/dist/assets/vue-flow-core.1180ec83.js +22 -0
  187. abstra_statics/dist/assets/{vue-quill.esm-bundler.37119951.js → vue-quill.esm-bundler.c4f04985.js} +2 -2
  188. abstra_statics/dist/assets/{workspaceStore.50ef2df1.js → workspaceStore.6244d03d.js} +2 -2
  189. abstra_statics/dist/assets/{xml.94b88503.js → xml.f2867af8.js} +3 -3
  190. abstra_statics/dist/assets/{yaml.73b7d5ce.js → yaml.5427bb1b.js} +2 -2
  191. abstra_statics/dist/console.html +14 -15
  192. abstra_statics/dist/editor.html +13 -13
  193. abstra_statics/dist/player.html +9 -9
  194. tests/e2e/test_crud_files.py +1 -0
  195. abstra_internals/templates/abstraignore +0 -8
  196. abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.441fcdfd.js +0 -2
  197. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.3f03a3ef.js +0 -2
  198. abstra_statics/dist/assets/ApiKeys.cb561e62.js +0 -2
  199. abstra_statics/dist/assets/App.dc82115f.js +0 -2
  200. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.3640ec1c.js +0 -2
  201. abstra_statics/dist/assets/BaseLayout.c63dfc2d.js +0 -2
  202. abstra_statics/dist/assets/ConnectorsView.b428e487.js +0 -2
  203. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.506f28c6.js +0 -2
  204. abstra_statics/dist/assets/ContentLayout.228d2328.js +0 -2
  205. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.61381525.js +0 -2
  206. abstra_statics/dist/assets/EnvVars.242b97c2.js +0 -2
  207. abstra_statics/dist/assets/ExclamationCircleOutlined.6d10f60b.js +0 -2
  208. abstra_statics/dist/assets/Home.80c7e349.js +0 -2
  209. abstra_statics/dist/assets/LoadingContainer.6e2b63e4.js +0 -2
  210. abstra_statics/dist/assets/Login.409f4a11.js +0 -2
  211. abstra_statics/dist/assets/Logo.2de02c4a.js +0 -2
  212. abstra_statics/dist/assets/Main.4370ed68.js +0 -2
  213. abstra_statics/dist/assets/NewEditor.769f4459.js +0 -8
  214. abstra_statics/dist/assets/NewEditor.d6e41a05.css +0 -1
  215. abstra_statics/dist/assets/OidcLoginCallback.a89857fe.js +0 -2
  216. abstra_statics/dist/assets/OidcLogoutCallback.d151c695.js +0 -2
  217. abstra_statics/dist/assets/OmniChat.0f64dfec.css +0 -1
  218. abstra_statics/dist/assets/OmniChat.d015bfa8.js +0 -6
  219. abstra_statics/dist/assets/PhCopySimple.vue.43c74ebe.js +0 -2
  220. abstra_statics/dist/assets/PhDownloadSimple.vue.c92aeaff.js +0 -2
  221. abstra_statics/dist/assets/PhPencilSimpleLine.vue.34633dfa.js +0 -2
  222. abstra_statics/dist/assets/Project.c03610d3.js +0 -2
  223. abstra_statics/dist/assets/ProjectLogin.934271a6.js +0 -2
  224. abstra_statics/dist/assets/Sql.3cdc910a.css +0 -1
  225. abstra_statics/dist/assets/Sql.b6aa38ca.js +0 -5
  226. abstra_statics/dist/assets/Steps.7c7e4a4a.js +0 -2
  227. abstra_statics/dist/assets/TableEditor.1e680eaf.css +0 -1
  228. abstra_statics/dist/assets/TableEditor.dc1b4a2d.js +0 -2
  229. abstra_statics/dist/assets/Tables.de30953b.js +0 -2
  230. abstra_statics/dist/assets/TablesDiagram.1ec45dd9.css +0 -1
  231. abstra_statics/dist/assets/TablesDiagram.97d6a43f.js +0 -15
  232. abstra_statics/dist/assets/View.617ad8d8.js +0 -2
  233. abstra_statics/dist/assets/WidgetPreview.99f14714.js +0 -2
  234. abstra_statics/dist/assets/ant-design.4952c8fb.js +0 -2
  235. abstra_statics/dist/assets/apiKey.864dc66b.js +0 -2
  236. abstra_statics/dist/assets/asyncComputed.25309626.js +0 -2
  237. abstra_statics/dist/assets/console.3d4702c3.js +0 -25
  238. abstra_statics/dist/assets/constants.56e8988f.js +0 -2
  239. abstra_statics/dist/assets/datetime.e5660676.js +0 -2
  240. abstra_statics/dist/assets/dayjs.c54f8edb.js +0 -2
  241. abstra_statics/dist/assets/editor.6d0baf6f.js +0 -2
  242. abstra_statics/dist/assets/editor.main.65812c73.js +0 -2
  243. abstra_statics/dist/assets/fetch.5136a62d.js +0 -2
  244. abstra_statics/dist/assets/folder.d7d65e5b.js +0 -2
  245. abstra_statics/dist/assets/index.23283fbb.js +0 -2
  246. abstra_statics/dist/assets/index.4b93c8ad.js +0 -2
  247. abstra_statics/dist/assets/index.58e29274.js +0 -2
  248. abstra_statics/dist/assets/linters.640d6098.js +0 -2
  249. abstra_statics/dist/assets/player.30593e18.js +0 -2
  250. abstra_statics/dist/assets/polling.d1c934c7.js +0 -2
  251. abstra_statics/dist/assets/record.4ffc477c.js +0 -2
  252. abstra_statics/dist/assets/repository.360feb8f.js +0 -2
  253. abstra_statics/dist/assets/repository.ab3036a9.js +0 -2
  254. abstra_statics/dist/assets/router.7f571832.js +0 -2
  255. abstra_statics/dist/assets/router.8fd5b2ad.js +0 -10
  256. abstra_statics/dist/assets/url.8583a595.js +0 -2
  257. abstra_statics/dist/assets/userStore.73b89fbb.js +0 -2
  258. abstra_statics/dist/assets/uuid.dadede91.js +0 -2
  259. abstra_statics/dist/assets/vue-flow-background.d2772d9a.js +0 -22
  260. {abstra-3.23.12.dist-info → abstra-3.24.0.dist-info}/WHEEL +0 -0
  261. {abstra-3.23.12.dist-info → abstra-3.24.0.dist-info}/entry_points.txt +0 -0
  262. {abstra-3.23.12.dist-info → abstra-3.24.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,267 @@
1
+ """
2
+ Git types and data structures shared across implementations.
3
+ """
4
+
5
+ from abc import ABC, abstractmethod
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Optional, Tuple
9
+
10
+
11
+ @dataclass
12
+ class GitCommit:
13
+ hash: str
14
+ message: str
15
+ author: str
16
+ date: str
17
+
18
+
19
+ @dataclass
20
+ class AheadBehindInfo:
21
+ ahead: int = 0
22
+ behind: int = 0
23
+
24
+
25
+ @dataclass
26
+ class RemoteGitStatus:
27
+ success: bool
28
+ remote_commit: Optional[str] = None
29
+ branch: str = "main"
30
+ has_commits: bool = False
31
+ available_branches: List[str] = field(default_factory=list)
32
+ message: Optional[str] = None
33
+ ahead_behind: Optional[AheadBehindInfo] = None
34
+ has_remote_changes: Optional[bool] = None
35
+ has_conflicts: Optional[bool] = None
36
+
37
+
38
+ @dataclass
39
+ class GitFileChange:
40
+ path: str
41
+ status: str # added, deleted, modified, renamed, untracked
42
+ status_code: str # Git porcelain status code (e.g., "M ", "A ", "??")
43
+
44
+
45
+ @dataclass
46
+ class GitStatusResponse:
47
+ available: bool
48
+ branch: str
49
+ branches: List[str]
50
+ last_commit: Optional[GitCommit]
51
+ has_changes: bool
52
+ changed_files: List[str]
53
+ changed_files_with_status: List[GitFileChange]
54
+ remote_status: Optional[RemoteGitStatus] = None
55
+
56
+ def to_dict(self) -> Dict[str, Any]:
57
+ result = {
58
+ "available": self.available,
59
+ "branch": self.branch,
60
+ "branches": self.branches,
61
+ "lastCommit": {
62
+ "hash": self.last_commit.hash,
63
+ "message": self.last_commit.message,
64
+ "author": self.last_commit.author,
65
+ "date": self.last_commit.date,
66
+ }
67
+ if self.last_commit
68
+ else None,
69
+ "hasChanges": self.has_changes,
70
+ "changedFiles": self.changed_files,
71
+ "changedFilesWithStatus": [
72
+ {
73
+ "path": file_change.path,
74
+ "status": file_change.status,
75
+ "statusCode": file_change.status_code,
76
+ }
77
+ for file_change in self.changed_files_with_status
78
+ ],
79
+ }
80
+
81
+ if self.remote_status:
82
+ result["remoteStatus"] = {
83
+ "success": self.remote_status.success,
84
+ "remoteCommit": self.remote_status.remote_commit,
85
+ "hasRemoteChanges": self.remote_status.has_remote_changes or False,
86
+ "hasConflicts": self.remote_status.has_conflicts or False,
87
+ "message": self.remote_status.message,
88
+ "availableBranches": self.remote_status.available_branches,
89
+ "aheadBehind": {
90
+ "ahead": self.remote_status.ahead_behind.ahead,
91
+ "behind": self.remote_status.ahead_behind.behind,
92
+ }
93
+ if self.remote_status.ahead_behind
94
+ else None,
95
+ }
96
+ else:
97
+ result["remoteStatus"] = None
98
+
99
+ return result
100
+
101
+
102
+ @dataclass
103
+ class GitStatus:
104
+ available: bool
105
+ branch: str = ""
106
+ branches: List[str] = field(default_factory=list)
107
+ last_commit: Optional[GitCommit] = None
108
+ has_changes: bool = False
109
+ changed_files: List[str] = field(default_factory=list)
110
+ changed_files_with_status: List[GitFileChange] = field(default_factory=list)
111
+ remote_status: Optional[RemoteGitStatus] = None
112
+
113
+
114
+ class GitRepositoryInterface(ABC):
115
+ """Abstract interface for Git repository implementations"""
116
+
117
+ def __init__(self, working_directory: Path):
118
+ self.working_directory = working_directory
119
+
120
+ @abstractmethod
121
+ def is_git_available(self) -> bool:
122
+ """Check if git functionality is available"""
123
+ pass
124
+
125
+ @abstractmethod
126
+ def is_git_repository(self) -> bool:
127
+ """Check if current directory is a git repository"""
128
+ pass
129
+
130
+ @abstractmethod
131
+ def find_git_root(self, start_path: Optional[Path] = None) -> Optional[Path]:
132
+ """Find the root directory of the git repository"""
133
+ pass
134
+
135
+ @abstractmethod
136
+ def configure_git_user(self, fallback_email: str, fallback_name: str):
137
+ """Ensure git user is configured for commits (required in CI environments)"""
138
+ pass
139
+
140
+ @abstractmethod
141
+ def get_current_branch(self) -> Optional[str]:
142
+ """Get the current branch name"""
143
+ pass
144
+
145
+ @abstractmethod
146
+ def get_all_branches(self) -> List[str]:
147
+ """Get all local branches"""
148
+ pass
149
+
150
+ @abstractmethod
151
+ def get_last_commit(self) -> Optional[GitCommit]:
152
+ """Get information about the last commit"""
153
+ pass
154
+
155
+ @abstractmethod
156
+ def get_commit_history(
157
+ self, limit: int = 10, offset: int = 0, branch: Optional[str] = None
158
+ ) -> List[GitCommit]:
159
+ """Get commit history with pagination"""
160
+ pass
161
+
162
+ @abstractmethod
163
+ def get_changed_files(self) -> List[str]:
164
+ """Get list of changed files"""
165
+ pass
166
+
167
+ @abstractmethod
168
+ def get_changed_files_with_status(self) -> List[GitFileChange]:
169
+ """Get list of changed files with their git status"""
170
+ pass
171
+
172
+ @abstractmethod
173
+ def has_uncommitted_changes(self) -> bool:
174
+ """Check if there are uncommitted changes"""
175
+ pass
176
+
177
+ @abstractmethod
178
+ def init_repository(self) -> bool:
179
+ """Initialize a new git repository"""
180
+ pass
181
+
182
+ @abstractmethod
183
+ def get_repository_status(self) -> GitStatus:
184
+ """Get comprehensive repository status"""
185
+ pass
186
+
187
+ @abstractmethod
188
+ def checkout_branch(self, branch_name: str) -> bool:
189
+ """Switch to a different branch"""
190
+ pass
191
+
192
+ @abstractmethod
193
+ def checkout_commit(self, commit_hash: str) -> bool:
194
+ """Switch to a specific commit (detached HEAD state)"""
195
+ pass
196
+
197
+ @abstractmethod
198
+ def pull_changes(
199
+ self,
200
+ strategy: str = "merge",
201
+ allow_unrelated: bool = True,
202
+ conflict_resolution: Optional[str] = "theirs",
203
+ ) -> bool:
204
+ """Pull changes from remote repository"""
205
+ pass
206
+
207
+ @abstractmethod
208
+ def commit_changes(self, message: str, add_all: bool = True) -> bool:
209
+ """Commit changes with a message"""
210
+ pass
211
+
212
+ @abstractmethod
213
+ def stash_changes(self, message: str = "WIP") -> bool:
214
+ """Stash uncommitted changes"""
215
+ pass
216
+
217
+ @abstractmethod
218
+ def get_remotes(self) -> List[str]:
219
+ """Get list of remote names"""
220
+ pass
221
+
222
+ @abstractmethod
223
+ def has_remote(self, remote_name: str) -> bool:
224
+ """Check if a remote exists"""
225
+ pass
226
+
227
+ @abstractmethod
228
+ def add_remote(self, remote_name: str, remote_url: str) -> bool:
229
+ """Add a remote to the repository"""
230
+ pass
231
+
232
+ @abstractmethod
233
+ def set_remote_url(self, remote_name: str, remote_url: str) -> bool:
234
+ """Set/update the URL for a remote"""
235
+ pass
236
+
237
+ @abstractmethod
238
+ def set_git_config(self, key: str, value: str, local: bool = True) -> bool:
239
+ """Set a git configuration value"""
240
+ pass
241
+
242
+ @abstractmethod
243
+ def get_git_config(self, key: str, local: bool = True) -> Optional[str]:
244
+ """Get a git configuration value"""
245
+ pass
246
+
247
+ @abstractmethod
248
+ def push_and_deploy(self, branch: str = "main") -> bool:
249
+ """Deploy to Abstra remote (push to abstra remote)"""
250
+ pass
251
+
252
+ @abstractmethod
253
+ def revert_commit(self, commit_hash: str) -> bool:
254
+ """Restore content from a previous commit by creating a new commit with that content"""
255
+ pass
256
+
257
+ @abstractmethod
258
+ def check_merge_conflicts(self, remote_commit: str) -> bool:
259
+ """Check if merging with remote commit would cause conflicts"""
260
+ pass
261
+
262
+ @abstractmethod
263
+ def get_ahead_behind_count(
264
+ self, local_commit: str, remote_commit: str
265
+ ) -> Tuple[int, int]:
266
+ """Calculate ahead/behind count between local and remote commits"""
267
+ pass
@@ -1,6 +1,6 @@
1
1
  from typing import List
2
2
 
3
- from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
3
+ from abstra_internals.consts.filepaths import GITIGNORE_FILEPATH
4
4
  from abstra_internals.repositories.linter.models import (
5
5
  LinterFix,
6
6
  LinterIssue,
@@ -10,11 +10,11 @@ from abstra_internals.services.fs import FileSystemService
10
10
  from abstra_internals.settings import Settings
11
11
 
12
12
 
13
- class AddEnvToAbstraIgnore(LinterFix):
14
- label = "Add env to abstra ignore"
13
+ class AddEnvToGitIgnore(LinterFix):
14
+ label = "Add env to git ignore"
15
15
 
16
16
  def fix(self):
17
- abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
17
+ abstraignore_file = Settings.root_path / GITIGNORE_FILEPATH
18
18
  with abstraignore_file.open("a") as file:
19
19
  file.write("\n.env")
20
20
 
@@ -22,7 +22,7 @@ class AddEnvToAbstraIgnore(LinterFix):
22
22
  class EnvInBundleFound(LinterIssue):
23
23
  def __init__(self) -> None:
24
24
  self.label = "You have not ignored the .env file"
25
- self.fixes = [AddEnvToAbstraIgnore()]
25
+ self.fixes = [AddEnvToGitIgnore()]
26
26
 
27
27
 
28
28
  class EnvInBundle(LinterRule):
@@ -1,6 +1,6 @@
1
1
  from unittest import TestCase
2
2
 
3
- from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
3
+ from abstra_internals.consts.filepaths import GITIGNORE_FILEPATH
4
4
  from abstra_internals.repositories.linter.rules.env_in_bundle import EnvInBundle
5
5
  from tests.fixtures import clear_dir, init_dir
6
6
 
@@ -21,21 +21,21 @@ class EnvInBundleTest(TestCase):
21
21
  def test_env_on_bundle_valid_with_env(self):
22
22
  rule = EnvInBundle()
23
23
  env_file = self.root / ".env"
24
- abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
24
+ abstraignore_file = self.root / GITIGNORE_FILEPATH
25
25
  abstraignore_file.write_text(".env")
26
26
  env_file.touch()
27
27
  self.assertEqual(len(rule.find_issues()), 0)
28
28
 
29
- def tet_env_on_bundle_invalid_without_abstraignore_file(self):
29
+ def test_env_on_bundle_invalid_without_gitignore_file(self):
30
30
  env_file = self.root / ".env"
31
31
  env_file.touch()
32
32
  rule = EnvInBundle()
33
33
  self.assertEqual(len(rule.find_issues()), 1)
34
34
 
35
- def test_env_on_bundle_invalid_with_abstraignore_file(self):
35
+ def test_env_on_bundle_invalid_with_gitignore_file(self):
36
36
  env_file = self.root / ".env"
37
37
  env_file.touch()
38
- abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
38
+ abstraignore_file = self.root / GITIGNORE_FILEPATH
39
39
  abstraignore_file.touch()
40
40
  rule = EnvInBundle()
41
41
  self.assertEqual(len(rule.find_issues()), 1)
@@ -47,7 +47,7 @@ class EnvInBundleTest(TestCase):
47
47
  self.assertEqual(len(rule.find_issues()), 1)
48
48
  rule.find_issues()[0].fixes[0].fix()
49
49
  self.assertEqual(len(rule.find_issues()), 0)
50
- abstraignore_file = self.root / ABSTRA_IGNORE_FILEPATH
50
+ abstraignore_file = self.root / GITIGNORE_FILEPATH
51
51
  self.assertTrue(abstraignore_file.exists())
52
52
  with abstraignore_file.open("r") as file:
53
53
  content = file.read()
@@ -2,12 +2,13 @@ import sys
2
2
  from pathlib import Path
3
3
  from typing import List
4
4
 
5
- from abstra_internals.consts.filepaths import ABSTRA_IGNORE_FILEPATH
5
+ from abstra_internals.consts.filepaths import GITIGNORE_FILEPATH
6
6
  from abstra_internals.repositories.linter.models import (
7
7
  LinterFix,
8
8
  LinterIssue,
9
9
  LinterRule,
10
10
  )
11
+ from abstra_internals.services.fs import FileSystemService
11
12
  from abstra_internals.settings import Settings
12
13
 
13
14
 
@@ -30,14 +31,14 @@ def running_under_virtualenv() -> bool:
30
31
  return False
31
32
 
32
33
 
33
- class AddVenvToAbstraIgnore(LinterFix):
34
- label = "Add virtual env to abstra ignore"
34
+ class AddVenvToGitIgnore(LinterFix):
35
+ label = "Add virtual env to git ignore"
35
36
 
36
37
  def fix(self):
37
38
  root_path, prefix_path = get_root_and_prefix_path()
38
39
  venv_folder = prefix_path.replace(root_path, "").lstrip("/")
39
40
 
40
- abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
41
+ abstraignore_file = Settings.root_path / GITIGNORE_FILEPATH
41
42
  with abstraignore_file.open("a") as file:
42
43
  file.write("\n")
43
44
  file.write(venv_folder)
@@ -46,7 +47,7 @@ class AddVenvToAbstraIgnore(LinterFix):
46
47
  class VenvInBundleFound(LinterIssue):
47
48
  def __init__(self) -> None:
48
49
  self.label = "You have not ignored the virtualenv folder"
49
- self.fixes = [AddVenvToAbstraIgnore()]
50
+ self.fixes = [AddVenvToGitIgnore()]
50
51
 
51
52
 
52
53
  class VenvInBundle(LinterRule):
@@ -57,19 +58,11 @@ class VenvInBundle(LinterRule):
57
58
  root_path, prefix_path = get_root_and_prefix_path()
58
59
  return prefix_path.startswith(root_path)
59
60
 
60
- def virtualenv_in_abstraignore(self) -> bool:
61
- abstraignore_file = Settings.root_path / ABSTRA_IGNORE_FILEPATH
62
- if not abstraignore_file.exists():
63
- return False
64
-
61
+ def virtualenv_ignored(self) -> bool:
65
62
  root_path, prefix_path = get_root_and_prefix_path()
66
63
  venv_folder = prefix_path.replace(root_path, "").lstrip("/")
67
64
 
68
- for line in abstraignore_file.read_text(encoding="utf-8").split("\n"):
69
- if line == venv_folder:
70
- return True
71
-
72
- return False
65
+ return FileSystemService.is_ignored(Path(venv_folder))
73
66
 
74
67
  def find_issues(self) -> List[LinterIssue]:
75
68
  if not running_under_virtualenv():
@@ -78,7 +71,7 @@ class VenvInBundle(LinterRule):
78
71
  if not self.virtualenv_inside_project():
79
72
  return []
80
73
 
81
- if self.virtualenv_in_abstraignore():
74
+ if self.virtualenv_ignored():
82
75
  return []
83
76
 
84
77
  return [VenvInBundleFound()]
@@ -11,6 +11,7 @@ from abstra_internals.server.routes import codebase as codebase_router
11
11
  from abstra_internals.server.routes import env_vars as envvars_router
12
12
  from abstra_internals.server.routes import executions as executions_router
13
13
  from abstra_internals.server.routes import forms as forms_router
14
+ from abstra_internals.server.routes import git as git_router
14
15
  from abstra_internals.server.routes import hooks as hooks_router
15
16
  from abstra_internals.server.routes import jobs as jobs_router
16
17
  from abstra_internals.server.routes import linters as linters_router
@@ -104,6 +105,9 @@ def _get_api_bp(controller: MainController):
104
105
  web_editor_bp = web_editor_router.get_web_editor_bp(controller)
105
106
  bp.register_blueprint(web_editor_bp, url_prefix="/web-editor")
106
107
 
108
+ git_bp = git_router.get_editor_bp(controller)
109
+ bp.register_blueprint(git_bp, url_prefix="/git")
110
+
107
111
  return bp
108
112
 
109
113
 
@@ -0,0 +1,190 @@
1
+ from typing import cast
2
+
3
+ import flask
4
+
5
+ from abstra_internals.controllers.git import EmailProvider, GitController
6
+ from abstra_internals.controllers.main import MainController
7
+ from abstra_internals.environment import EDITOR_MODE
8
+ from abstra_internals.usage import editor_usage
9
+
10
+
11
+ def get_editor_bp(controller: MainController):
12
+ bp = flask.Blueprint("editor_git", __name__)
13
+ if EDITOR_MODE == "local":
14
+ # `MainController` implements the get_email method at runtime, but the
15
+ # static type of `MainController` may not be recognized as an
16
+ # `EmailProvider` by the type checker.
17
+ git_controller = GitController(email_provider=cast(EmailProvider, controller))
18
+ else:
19
+ git_controller = GitController()
20
+
21
+ @bp.get("/status")
22
+ @editor_usage
23
+ def _get_git_status():
24
+ try:
25
+ status = git_controller.get_status()
26
+ return flask.jsonify(status)
27
+ except Exception as e:
28
+ return flask.jsonify({"available": False, "error": str(e)}), 500
29
+
30
+ @bp.post("/checkout")
31
+ @editor_usage
32
+ def _checkout_branch():
33
+ if not flask.request.json:
34
+ flask.abort(400)
35
+
36
+ branch_name = flask.request.json.get("branch")
37
+ commit_hash = flask.request.json.get("commitHash")
38
+
39
+ if not branch_name and not commit_hash:
40
+ return flask.jsonify(
41
+ {"success": False, "message": "Branch name or commit hash is required"}
42
+ ), 400
43
+
44
+ if branch_name and commit_hash:
45
+ return flask.jsonify(
46
+ {
47
+ "success": False,
48
+ "message": "Provide either branch or commit, not both",
49
+ }
50
+ ), 400
51
+
52
+ try:
53
+ if branch_name:
54
+ result = git_controller.checkout_branch(branch_name)
55
+ else:
56
+ result = git_controller.checkout_commit(commit_hash)
57
+
58
+ status_code = 200 if result["success"] else 400
59
+ return flask.jsonify(result), status_code
60
+ except Exception as e:
61
+ return flask.jsonify({"success": False, "message": str(e)}), 500
62
+
63
+ @bp.post("/pull")
64
+ @editor_usage
65
+ def _pull_changes():
66
+ try:
67
+ data = flask.request.json
68
+ if data is None:
69
+ flask.abort(400)
70
+
71
+ strategy = data.get("strategy", "merge")
72
+ allow_unrelated = data.get("allowUnrelated", True)
73
+ conflict_resolution = data.get("conflictResolution", "theirs")
74
+
75
+ result = git_controller.pull_changes(
76
+ strategy=strategy,
77
+ allow_unrelated=allow_unrelated,
78
+ conflict_resolution=conflict_resolution,
79
+ )
80
+ status_code = 200 if result["success"] else 400
81
+ return flask.jsonify(result), status_code
82
+ except Exception as e:
83
+ return flask.jsonify({"success": False, "message": str(e)}), 500
84
+
85
+ @bp.post("/commit")
86
+ @editor_usage
87
+ def _commit_changes():
88
+ if not flask.request.json:
89
+ flask.abort(400)
90
+
91
+ message = flask.request.json.get("message")
92
+ if not message:
93
+ return flask.jsonify(
94
+ {"success": False, "message": "Commit message is required"}
95
+ ), 400
96
+
97
+ try:
98
+ result = git_controller.commit_changes(message)
99
+ status_code = 200 if result["success"] else 400
100
+ return flask.jsonify(result), status_code
101
+ except Exception as e:
102
+ return flask.jsonify({"success": False, "message": str(e)}), 500
103
+
104
+ @bp.post("/stash")
105
+ @editor_usage
106
+ def _stash_changes():
107
+ try:
108
+ message = "Discarding WIP changes"
109
+
110
+ if flask.request.is_json and flask.request.json:
111
+ message = flask.request.json.get("message", "Discarding WIP changes")
112
+
113
+ result = git_controller.stash_changes(message)
114
+ status_code = 200 if result["success"] else 400
115
+ return flask.jsonify(result), status_code
116
+ except Exception as e:
117
+ return flask.jsonify({"success": False, "message": str(e)}), 500
118
+
119
+ @bp.get("/commits")
120
+ @editor_usage
121
+ def _get_commit_history():
122
+ try:
123
+ limit = int(flask.request.args.get("limit", 200))
124
+ offset = int(flask.request.args.get("offset", 0))
125
+ branch = flask.request.args.get("branch")
126
+
127
+ result = git_controller.get_commit_history(
128
+ limit=limit, offset=offset, branch=branch
129
+ )
130
+ return flask.jsonify(result), 200
131
+ except Exception as e:
132
+ return flask.jsonify(
133
+ {"commits": [], "hasMore": False, "error": str(e)}
134
+ ), 500
135
+
136
+ @bp.post("/deploy")
137
+ @editor_usage
138
+ def _deploy_to_abstra():
139
+ try:
140
+ controller.linter_repository.update_checks()
141
+ issues = controller.linter_repository.get_blocking_checks()
142
+
143
+ if len(issues) > 0:
144
+ return flask.jsonify(
145
+ {
146
+ "success": False,
147
+ "message": "Please fix all linter issues before deploying your project.",
148
+ }
149
+ ), 400
150
+
151
+ data = flask.request.get_json(silent=True)
152
+ branch = data.get("branch", "main") if data else "main"
153
+
154
+ result = git_controller.push_and_deploy(branch)
155
+ status_code = 200 if result["success"] else 400
156
+ return flask.jsonify(result), status_code
157
+ except Exception as e:
158
+ return flask.jsonify({"success": False, "message": str(e)}), 500
159
+
160
+ @bp.get("/remotes")
161
+ @editor_usage
162
+ def _get_remotes():
163
+ try:
164
+ result = git_controller.get_remotes()
165
+ return flask.jsonify(result), 200
166
+ except Exception as e:
167
+ return flask.jsonify(
168
+ {"remotes": [], "hasAbstraRemote": False, "error": str(e)}
169
+ ), 500
170
+
171
+ @bp.post("/revert")
172
+ @editor_usage
173
+ def _revert_commit():
174
+ if not flask.request.json:
175
+ flask.abort(400)
176
+
177
+ commit_hash = flask.request.json.get("commitHash")
178
+ if not commit_hash:
179
+ return flask.jsonify(
180
+ {"success": False, "message": "Commit hash is required"}
181
+ ), 400
182
+
183
+ try:
184
+ result = git_controller.revert_commit(commit_hash)
185
+ status_code = 200 if result["success"] else 400
186
+ return flask.jsonify(result), status_code
187
+ except Exception as e:
188
+ return flask.jsonify({"success": False, "message": str(e)}), 500
189
+
190
+ return bp
@@ -112,7 +112,7 @@ def get_editor_bp(controller: MainController):
112
112
  @bp.post("/deploy")
113
113
  @editor_usage
114
114
  def _deploy():
115
- controller.deploy()
115
+ controller.deploy_without_git()
116
116
  return {"success": True}
117
117
 
118
118
  return bp