abstra 3.23.12__py3-none-any.whl → 3.24.1__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 (266) hide show
  1. abstra/cli.py +16 -5
  2. {abstra-3.23.12.dist-info → abstra-3.24.1.dist-info}/METADATA +1 -1
  3. {abstra-3.23.12.dist-info → abstra-3.24.1.dist-info}/RECORD +200 -190
  4. abstra_internals/cloud_api/__init__.py +17 -8
  5. abstra_internals/consts/filepaths.py +1 -1
  6. abstra_internals/contracts_generated.py +616 -596
  7. abstra_internals/controllers/ai.py +89 -1
  8. abstra_internals/controllers/git.py +307 -0
  9. abstra_internals/controllers/main.py +10 -6
  10. abstra_internals/environment.py +6 -0
  11. abstra_internals/interface/cli/deploy.py +1 -1
  12. abstra_internals/interface/cli/editor.py +2 -2
  13. abstra_internals/interface/sdk/ai.py +1 -0
  14. abstra_internals/repositories/git/__init__.py +25 -0
  15. abstra_internals/repositories/git/git_test.py +362 -0
  16. abstra_internals/repositories/git/native.py +578 -0
  17. abstra_internals/repositories/git/types.py +273 -0
  18. abstra_internals/repositories/linter/rules/env_in_bundle.py +5 -5
  19. abstra_internals/repositories/linter/rules/env_in_bundle_test.py +6 -6
  20. abstra_internals/repositories/linter/rules/venv_in_bundle.py +9 -16
  21. abstra_internals/server/blueprints/editor.py +4 -0
  22. abstra_internals/server/routes/git.py +190 -0
  23. abstra_internals/server/routes/workspace.py +1 -1
  24. abstra_internals/services/file_watcher.py +32 -13
  25. abstra_internals/services/fs.py +4 -4
  26. abstra_internals/services/fs_test.py +4 -6
  27. abstra_internals/templates/__init__.py +0 -11
  28. abstra_statics/dist/assets/{AbstraButton.vue_vue_type_script_setup_true_lang.441fcdfd.js → AbstraButton.vue_vue_type_script_setup_true_lang.aefce2d3.js} +2 -2
  29. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.28bc9fc9.js +2 -0
  30. abstra_statics/dist/assets/ApiKeys.3c1e70dc.js +2 -0
  31. abstra_statics/dist/assets/App.9d8bd2aa.js +2 -0
  32. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.a64c7cee.js +2 -0
  33. abstra_statics/dist/assets/{BaseLayout.c63dfc2d.js → BaseLayout.72a6c8f2.js} +2 -2
  34. abstra_statics/dist/assets/{Billing.461db3ef.js → Billing.6b07f282.js} +2 -2
  35. abstra_statics/dist/assets/{Breadcrumb.27110ec4.js → Breadcrumb.5c786c09.js} +2 -2
  36. abstra_statics/dist/assets/{Builds.4eecd717.js → Builds.d8c5c61b.js} +2 -2
  37. abstra_statics/dist/assets/{Card.1d1a9fb7.js → Card.fc77085c.js} +2 -2
  38. abstra_statics/dist/assets/{CircularLoading.fc66331b.js → CircularLoading.998b223a.js} +2 -2
  39. abstra_statics/dist/assets/{CloseCircleOutlined.0110bbe2.js → CloseCircleOutlined.9ff269cc.js} +2 -2
  40. abstra_statics/dist/assets/{ConnectorsView.82e74ae4.css → ConnectorsView.33c5380f.css} +1 -1
  41. abstra_statics/dist/assets/ConnectorsView.e670738b.js +2 -0
  42. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.3ca66327.js +2 -0
  43. abstra_statics/dist/assets/ContentLayout.0db8d7fb.js +2 -0
  44. abstra_statics/dist/assets/{CrudView.c16e2f81.js → CrudView.cc6aeaf0.js} +2 -2
  45. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.689d8ce7.js +2 -0
  46. abstra_statics/dist/assets/{EditorLogin.00a3b3d9.js → EditorLogin.691452b4.js} +2 -2
  47. abstra_statics/dist/assets/{EditorsView.f7b2843c.js → EditorsView.fcdf0186.js} +2 -2
  48. abstra_statics/dist/assets/EnvVars.5519b4ce.js +2 -0
  49. abstra_statics/dist/assets/Error.11fd1f21.js +2 -0
  50. abstra_statics/dist/assets/ExclamationCircleOutlined.d0aa47da.js +2 -0
  51. abstra_statics/dist/assets/Files.6ea18d46.js +2 -0
  52. abstra_statics/dist/assets/{Form.8077681f.js → Form.09de384c.js} +2 -2
  53. abstra_statics/dist/assets/{FormRunner.055e2c45.js → FormRunner.15be6fa0.js} +2 -2
  54. abstra_statics/dist/assets/{Home.0ef22910.js → Home.782c5702.js} +2 -2
  55. abstra_statics/dist/assets/Home.7c495aa9.js +2 -0
  56. abstra_statics/dist/assets/{Live.e1261458.js → Live.48c10605.js} +2 -2
  57. abstra_statics/dist/assets/LoadingContainer.82ac3d44.js +2 -0
  58. abstra_statics/dist/assets/LoadingOutlined.66639b08.js +2 -0
  59. abstra_statics/dist/assets/Login.4a05f5c5.js +2 -0
  60. abstra_statics/dist/assets/{Login.041361ea.js → Login.4e852955.js} +2 -2
  61. abstra_statics/dist/assets/{Login.vue_vue_type_script_setup_true_lang.c2eb444c.js → Login.vue_vue_type_script_setup_true_lang.5161f328.js} +2 -2
  62. abstra_statics/dist/assets/Logo.65e0b37c.js +2 -0
  63. abstra_statics/dist/assets/{Logs.f76cde12.js → Logs.7d1acf32.js} +2 -2
  64. abstra_statics/dist/assets/{LogsController.addd81bf.js → LogsController.0eb2eb30.js} +2 -2
  65. abstra_statics/dist/assets/Main.0313f4fc.js +2 -0
  66. abstra_statics/dist/assets/{MockForm.c9441864.js → MockForm.0b819a52.js} +2 -2
  67. abstra_statics/dist/assets/Navbar.ef64aa09.js +2 -0
  68. abstra_statics/dist/assets/NewEditor.b9c410fe.css +1 -0
  69. abstra_statics/dist/assets/NewEditor.c928dbe9.js +8 -0
  70. abstra_statics/dist/assets/OidcLoginCallback.42a7c4f9.js +2 -0
  71. abstra_statics/dist/assets/OidcLogoutCallback.35931a68.js +2 -0
  72. abstra_statics/dist/assets/OmniChat.73d9f6bb.js +6 -0
  73. abstra_statics/dist/assets/OmniChat.a21fec40.css +1 -0
  74. abstra_statics/dist/assets/{OnboardingView.fbc4b6fe.js → OnboardingView.25042384.js} +2 -2
  75. abstra_statics/dist/assets/{Organization.bc495099.js → Organization.66f64939.js} +2 -2
  76. abstra_statics/dist/assets/Organizations.47868beb.js +2 -0
  77. abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.156bcd89.js → PhArrowCounterClockwise.vue.78877a50.js} +2 -2
  78. abstra_statics/dist/assets/{PhArrowSquareOut.vue.d0c95a06.js → PhArrowSquareOut.vue.2ddfc219.js} +2 -2
  79. abstra_statics/dist/assets/{PhBookBookmark.vue.42e49494.js → PhBookBookmark.vue.41108214.js} +2 -2
  80. abstra_statics/dist/assets/{PhChats.vue.54d692e4.js → PhChats.vue.71e5cb89.js} +2 -2
  81. abstra_statics/dist/assets/{PhClockCounterClockwise.vue.d47d66ba.js → PhClockCounterClockwise.vue.854c3cb5.js} +2 -2
  82. abstra_statics/dist/assets/{PhCopy.vue.a0d9b0ec.js → PhCopy.vue.d116ddfb.js} +2 -2
  83. abstra_statics/dist/assets/{PhCopySimple.vue.43c74ebe.js → PhCopySimple.vue.5d0f839d.js} +2 -2
  84. abstra_statics/dist/assets/{PhCube.vue.498c014d.js → PhCube.vue.abf4a034.js} +2 -2
  85. abstra_statics/dist/assets/PhDatabase.vue.a94d95f6.js +2 -0
  86. abstra_statics/dist/assets/{PhDotsThreeVertical.vue.0ea03d82.js → PhDotsThreeVertical.vue.5e8ae2a9.js} +2 -2
  87. abstra_statics/dist/assets/{PhDownloadSimple.vue.c92aeaff.js → PhDownloadSimple.vue.e88f64f1.js} +2 -2
  88. abstra_statics/dist/assets/{PhFolderPlus.vue.0c210f8d.js → PhFolderPlus.vue.21d377a3.js} +2 -2
  89. abstra_statics/dist/assets/{PhGear.vue.86c3014a.js → PhGear.vue.363dd83c.js} +2 -2
  90. abstra_statics/dist/assets/{PhKey.vue.72ce23d3.js → PhKey.vue.23f3a465.js} +2 -2
  91. abstra_statics/dist/assets/{PhPencil.vue.80ed4b2e.js → PhPencil.vue.feb383e2.js} +2 -2
  92. abstra_statics/dist/assets/{PhPencilSimple.vue.0046d784.js → PhPencilSimple.vue.ec0eebb4.js} +2 -2
  93. abstra_statics/dist/assets/{PhRocket.vue.f3302a7e.js → PhRocket.vue.261a42b2.js} +2 -2
  94. abstra_statics/dist/assets/{PhSignOut.vue.c9150da4.js → PhSignOut.vue.d1d6498f.js} +2 -2
  95. abstra_statics/dist/assets/{PhSparkle.vue.84ea95fc.js → PhSparkle.vue.b6712d34.js} +2 -2
  96. abstra_statics/dist/assets/{PhUserList.vue.bce47902.js → PhUserList.vue.e0b8c21b.js} +2 -2
  97. abstra_statics/dist/assets/{PhUsersThree.vue.0a84dfa4.js → PhUsersThree.vue.8889f957.js} +2 -2
  98. abstra_statics/dist/assets/PhWarningCircle.vue.2087be7d.js +2 -0
  99. abstra_statics/dist/assets/{PhWebhooksLogo.vue.a704632d.js → PhWebhooksLogo.vue.d164db1e.js} +2 -2
  100. abstra_statics/dist/assets/{PlayerConfigProvider.26b585a1.js → PlayerConfigProvider.28e29025.js} +2 -2
  101. abstra_statics/dist/assets/{PlayerNavbar.0fa6f760.js → PlayerNavbar.aaad1dfd.js} +2 -2
  102. abstra_statics/dist/assets/Project.2de506d7.js +2 -0
  103. abstra_statics/dist/assets/{ProjectLogin.934271a6.js → ProjectLogin.22c8b093.js} +2 -2
  104. abstra_statics/dist/assets/{ProjectSettings.2bf7e6c8.js → ProjectSettings.d065c292.js} +2 -2
  105. abstra_statics/dist/assets/{ProjectsView.ed31b921.js → ProjectsView.cbd118ac.js} +2 -2
  106. abstra_statics/dist/assets/{SaveButton.fdf70b31.js → SaveButton.428227de.js} +2 -2
  107. abstra_statics/dist/assets/ScrollArea.vue_vue_type_script_setup_true_lang.6d724b05.js +2 -0
  108. abstra_statics/dist/assets/{Sidebar.e69f49bd.css → Sidebar.29baeab0.css} +1 -1
  109. abstra_statics/dist/assets/{Sidebar.781afa0a.js → Sidebar.41173d8e.js} +2 -2
  110. abstra_statics/dist/assets/Sql.90e6e2ba.css +1 -0
  111. abstra_statics/dist/assets/Sql.c070dd2c.js +5 -0
  112. abstra_statics/dist/assets/Steps.dd51a108.js +2 -0
  113. abstra_statics/dist/assets/TableCard.529112b9.css +1 -0
  114. abstra_statics/dist/assets/TableCard.c9a1cf41.js +2 -0
  115. abstra_statics/dist/assets/TableEditor.1bc47a95.js +2 -0
  116. abstra_statics/dist/assets/TableEditor.5853a363.css +1 -0
  117. abstra_statics/dist/assets/Tables.eff119eb.js +2 -0
  118. abstra_statics/dist/assets/TablesDiagram.a588e7ff.css +1 -0
  119. abstra_statics/dist/assets/TablesDiagram.d633acaf.js +15 -0
  120. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.cf7e34c5.js +2 -0
  121. abstra_statics/dist/assets/{Tasks.ee450480.js → Tasks.c311b05f.js} +2 -2
  122. abstra_statics/dist/assets/{UploadOutlined.e3072945.js → UploadOutlined.9aa845de.js} +2 -2
  123. abstra_statics/dist/assets/View.dd7ba25a.js +2 -0
  124. abstra_statics/dist/assets/{View.vue_vue_type_script_setup_true_lang.ee5d447b.js → View.vue_vue_type_script_setup_true_lang.e6563207.js} +2 -2
  125. abstra_statics/dist/assets/{Watermark.891eee9f.js → Watermark.9c9358e3.js} +2 -2
  126. abstra_statics/dist/assets/{WebEditor.7409cd48.js → WebEditor.83f2a960.js} +2 -2
  127. abstra_statics/dist/assets/WidgetPreview.7c6a231c.js +2 -0
  128. abstra_statics/dist/assets/ant-design.42419021.js +2 -0
  129. abstra_statics/dist/assets/apiKey.403049eb.js +2 -0
  130. abstra_statics/dist/assets/asyncComputed.47aec2b5.js +2 -0
  131. abstra_statics/dist/assets/{build.8fa1a961.js → build.45b511b6.js} +2 -2
  132. abstra_statics/dist/assets/colorHelpers.50ced1e9.js +2 -0
  133. abstra_statics/dist/assets/console.f0f09e6f.js +17 -0
  134. abstra_statics/dist/assets/constants.d569f33d.js +2 -0
  135. abstra_statics/dist/assets/{contracts.generated.c4057ed0.js → contracts.generated.aefe8d0a.js} +2 -2
  136. abstra_statics/dist/assets/{cssMode.4c65b876.js → cssMode.23271d4a.js} +2 -2
  137. abstra_statics/dist/assets/{datetime.e5660676.js → datetime.82608dfb.js} +2 -2
  138. abstra_statics/dist/assets/dayjs.7f71c60a.js +2 -0
  139. abstra_statics/dist/assets/editor.02184ced.js +2 -0
  140. abstra_statics/dist/assets/editor.main.0f931e87.js +2 -0
  141. abstra_statics/dist/assets/fetch.1b3d01a0.js +2 -0
  142. abstra_statics/dist/assets/files.8b58ee7e.js +2 -0
  143. abstra_statics/dist/assets/folder.13d3acac.js +2 -0
  144. abstra_statics/dist/assets/{freemarker2.1d872d48.js → freemarker2.f79b510b.js} +2 -2
  145. abstra_statics/dist/assets/{handlebars.41fc6db8.js → handlebars.07d5febe.js} +2 -2
  146. abstra_statics/dist/assets/{html.967e3c6d.js → html.7d6a59b4.js} +3 -3
  147. abstra_statics/dist/assets/{htmlMode.d38ab72a.js → htmlMode.7779ff94.js} +2 -2
  148. abstra_statics/dist/assets/{index.8e871bae.js → index.482df04a.js} +2 -2
  149. abstra_statics/dist/assets/{index.4b93c8ad.js → index.581e2edd.js} +2 -2
  150. abstra_statics/dist/assets/index.966ac1b9.js +2 -0
  151. abstra_statics/dist/assets/{index.9021ba5d.js → index.c7a996a6.js} +2 -2
  152. abstra_statics/dist/assets/{index.03d222dd.js → index.c8a56795.js} +2 -2
  153. abstra_statics/dist/assets/{index.5f3f38ed.js → index.d31da2c3.js} +2 -2
  154. abstra_statics/dist/assets/{index.51dbb698.js → index.d53182ed.js} +2 -2
  155. abstra_statics/dist/assets/{index.b762f5e8.js → index.e71a5f84.js} +2 -2
  156. abstra_statics/dist/assets/{javascript.0935bea2.js → javascript.4c528c2c.js} +3 -3
  157. abstra_statics/dist/assets/{jsonMode.2860b71c.js → jsonMode.d6441e9d.js} +2 -2
  158. abstra_statics/dist/assets/{jwt-decode.esm.7f2ef0df.js → jwt-decode.esm.54a1ea22.js} +8 -8
  159. abstra_statics/dist/assets/linters.a0f2aa84.js +2 -0
  160. abstra_statics/dist/assets/{liquid.1bae5f6b.js → liquid.0c441ad2.js} +2 -2
  161. abstra_statics/dist/assets/member.2260c37e.js +2 -0
  162. abstra_statics/dist/assets/{metadata.39f9b9ba.js → metadata.0098e20c.js} +2 -2
  163. abstra_statics/dist/assets/omniChatStore.dd7a1c46.js +8 -0
  164. abstra_statics/dist/assets/{organization.a877b653.js → organization.668d1b58.js} +2 -2
  165. abstra_statics/dist/assets/player.4ab4aed5.js +2 -0
  166. abstra_statics/dist/assets/{plotly.min.d3f75723.js → plotly.min.2a87d7e2.js} +2 -2
  167. abstra_statics/dist/assets/polling.b5a32c22.js +2 -0
  168. abstra_statics/dist/assets/{project.d22a89ee.js → project.1b8374f4.js} +2 -2
  169. abstra_statics/dist/assets/{python.4c1a1300.js → python.7ac27a56.js} +3 -3
  170. abstra_statics/dist/assets/{razor.ba8bdb33.js → razor.860a0279.js} +2 -2
  171. abstra_statics/dist/assets/{record.4ffc477c.js → record.e83a2eb2.js} +2 -2
  172. abstra_statics/dist/assets/{redirect.ce3c0f65.js → redirect.586749f4.js} +2 -2
  173. abstra_statics/dist/assets/{repository.360feb8f.js → repository.3a331f0f.js} +2 -2
  174. abstra_statics/dist/assets/{repository.ab3036a9.js → repository.66c7567c.js} +2 -2
  175. abstra_statics/dist/assets/router.06ee2b9d.js +2 -0
  176. abstra_statics/dist/assets/router.4dfadf5d.js +18 -0
  177. abstra_statics/dist/assets/string.61e11a30.js +2 -0
  178. abstra_statics/dist/assets/{tables.be1c51f8.js → tables.1bcb1140.js} +2 -2
  179. abstra_statics/dist/assets/{tasksController.bf12e264.js → tasksController.f3adf725.js} +2 -2
  180. abstra_statics/dist/assets/{toggleHighContrast.c4e1b24d.js → toggleHighContrast.9535bf54.js} +7 -7
  181. abstra_statics/dist/assets/{tsMode.da264aae.js → tsMode.c0f9fe1a.js} +2 -2
  182. abstra_statics/dist/assets/{typescript.db5d7517.js → typescript.310eae6d.js} +3 -3
  183. abstra_statics/dist/assets/url.f5664225.js +2 -0
  184. abstra_statics/dist/assets/{useCodebaseEvents.42573b00.js → useCodebaseEvents.276cc8f0.js} +2 -2
  185. abstra_statics/dist/assets/useTables.18fc3efd.js +2 -0
  186. abstra_statics/dist/assets/userStore.6ab1e5ac.js +2 -0
  187. abstra_statics/dist/assets/uuid.5b8ba8af.js +2 -0
  188. abstra_statics/dist/assets/vue-flow-background.af096866.js +2 -0
  189. abstra_statics/dist/assets/vue-flow-core.6cb99d32.js +22 -0
  190. abstra_statics/dist/assets/{vue-quill.esm-bundler.37119951.js → vue-quill.esm-bundler.e3d34d0e.js} +2 -2
  191. abstra_statics/dist/assets/{workspaceStore.50ef2df1.js → workspaceStore.fac1e9a9.js} +2 -2
  192. abstra_statics/dist/assets/{xml.94b88503.js → xml.04864db4.js} +3 -3
  193. abstra_statics/dist/assets/{yaml.73b7d5ce.js → yaml.f30375fe.js} +3 -3
  194. abstra_statics/dist/console.html +14 -15
  195. abstra_statics/dist/editor.html +13 -13
  196. abstra_statics/dist/player.html +9 -9
  197. tests/e2e/test_crud_files.py +1 -0
  198. abstra_internals/templates/abstraignore +0 -8
  199. abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.3f03a3ef.js +0 -2
  200. abstra_statics/dist/assets/ApiKeys.cb561e62.js +0 -2
  201. abstra_statics/dist/assets/App.dc82115f.js +0 -2
  202. abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.3640ec1c.js +0 -2
  203. abstra_statics/dist/assets/ConnectorsView.b428e487.js +0 -2
  204. abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.506f28c6.js +0 -2
  205. abstra_statics/dist/assets/ContentLayout.228d2328.js +0 -2
  206. abstra_statics/dist/assets/DocsButton.vue_vue_type_script_setup_true_lang.61381525.js +0 -2
  207. abstra_statics/dist/assets/EnvVars.242b97c2.js +0 -2
  208. abstra_statics/dist/assets/Error.466dbb94.js +0 -2
  209. abstra_statics/dist/assets/ExclamationCircleOutlined.6d10f60b.js +0 -2
  210. abstra_statics/dist/assets/Files.104385dd.js +0 -2
  211. abstra_statics/dist/assets/Home.80c7e349.js +0 -2
  212. abstra_statics/dist/assets/LoadingContainer.6e2b63e4.js +0 -2
  213. abstra_statics/dist/assets/LoadingOutlined.cd84d9c9.js +0 -2
  214. abstra_statics/dist/assets/Login.409f4a11.js +0 -2
  215. abstra_statics/dist/assets/Logo.2de02c4a.js +0 -2
  216. abstra_statics/dist/assets/Main.4370ed68.js +0 -2
  217. abstra_statics/dist/assets/Navbar.07ba9452.js +0 -2
  218. abstra_statics/dist/assets/NewEditor.769f4459.js +0 -8
  219. abstra_statics/dist/assets/NewEditor.d6e41a05.css +0 -1
  220. abstra_statics/dist/assets/OidcLoginCallback.a89857fe.js +0 -2
  221. abstra_statics/dist/assets/OidcLogoutCallback.d151c695.js +0 -2
  222. abstra_statics/dist/assets/OmniChat.0f64dfec.css +0 -1
  223. abstra_statics/dist/assets/OmniChat.d015bfa8.js +0 -6
  224. abstra_statics/dist/assets/Organizations.b630803f.js +0 -2
  225. abstra_statics/dist/assets/PhPencilSimpleLine.vue.34633dfa.js +0 -2
  226. abstra_statics/dist/assets/Project.c03610d3.js +0 -2
  227. abstra_statics/dist/assets/ScrollArea.vue_vue_type_script_setup_true_lang.3bbea426.js +0 -2
  228. abstra_statics/dist/assets/Sql.3cdc910a.css +0 -1
  229. abstra_statics/dist/assets/Sql.b6aa38ca.js +0 -5
  230. abstra_statics/dist/assets/Steps.7c7e4a4a.js +0 -2
  231. abstra_statics/dist/assets/TableEditor.1e680eaf.css +0 -1
  232. abstra_statics/dist/assets/TableEditor.dc1b4a2d.js +0 -2
  233. abstra_statics/dist/assets/Tables.de30953b.js +0 -2
  234. abstra_statics/dist/assets/TablesDiagram.1ec45dd9.css +0 -1
  235. abstra_statics/dist/assets/TablesDiagram.97d6a43f.js +0 -15
  236. abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.63aa07d0.js +0 -2
  237. abstra_statics/dist/assets/View.617ad8d8.js +0 -2
  238. abstra_statics/dist/assets/WidgetPreview.99f14714.js +0 -2
  239. abstra_statics/dist/assets/ant-design.4952c8fb.js +0 -2
  240. abstra_statics/dist/assets/apiKey.864dc66b.js +0 -2
  241. abstra_statics/dist/assets/asyncComputed.25309626.js +0 -2
  242. abstra_statics/dist/assets/colorHelpers.71d6d61d.js +0 -2
  243. abstra_statics/dist/assets/console.3d4702c3.js +0 -25
  244. abstra_statics/dist/assets/constants.56e8988f.js +0 -2
  245. abstra_statics/dist/assets/dayjs.c54f8edb.js +0 -2
  246. abstra_statics/dist/assets/editor.6d0baf6f.js +0 -2
  247. abstra_statics/dist/assets/editor.main.65812c73.js +0 -2
  248. abstra_statics/dist/assets/fetch.5136a62d.js +0 -2
  249. abstra_statics/dist/assets/folder.d7d65e5b.js +0 -2
  250. abstra_statics/dist/assets/index.23283fbb.js +0 -2
  251. abstra_statics/dist/assets/index.58e29274.js +0 -2
  252. abstra_statics/dist/assets/linters.640d6098.js +0 -2
  253. abstra_statics/dist/assets/member.b7ac8000.js +0 -2
  254. abstra_statics/dist/assets/omniChatStore.3431c026.js +0 -8
  255. abstra_statics/dist/assets/player.30593e18.js +0 -2
  256. abstra_statics/dist/assets/polling.d1c934c7.js +0 -2
  257. abstra_statics/dist/assets/router.7f571832.js +0 -2
  258. abstra_statics/dist/assets/router.8fd5b2ad.js +0 -10
  259. abstra_statics/dist/assets/string.2ed1cde3.js +0 -2
  260. abstra_statics/dist/assets/url.8583a595.js +0 -2
  261. abstra_statics/dist/assets/userStore.73b89fbb.js +0 -2
  262. abstra_statics/dist/assets/uuid.dadede91.js +0 -2
  263. abstra_statics/dist/assets/vue-flow-background.d2772d9a.js +0 -22
  264. {abstra-3.23.12.dist-info → abstra-3.24.1.dist-info}/WHEEL +0 -0
  265. {abstra-3.23.12.dist-info → abstra-3.24.1.dist-info}/entry_points.txt +0 -0
  266. {abstra-3.23.12.dist-info → abstra-3.24.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,578 @@
1
+ import shutil
2
+ import subprocess
3
+ from pathlib import Path
4
+ from typing import List, Optional, Tuple
5
+
6
+ from abstra_internals.environment import REMOTE_NAME
7
+
8
+ from .types import GitCommit, GitFileChange, GitRepositoryInterface, GitStatus
9
+
10
+ TEMP_ABSTRA_EMAIL = "abstra@abstra.app"
11
+ TEMP_ABSTRA_NAME = "Abstra"
12
+
13
+
14
+ class NativeGitRepository(GitRepositoryInterface):
15
+ """Repository for Git operations using system git command"""
16
+
17
+ def __init__(self, working_directory: Path):
18
+ super().__init__(working_directory)
19
+ self._git_available = None
20
+
21
+ def _run_git_command(
22
+ self, command: List[str], cwd: Optional[Path] = None
23
+ ) -> Tuple[bool, str, str]:
24
+ """Run a git command and return success, stdout, stderr"""
25
+ try:
26
+ if cwd is None:
27
+ cwd = self.working_directory
28
+
29
+ result = subprocess.run(
30
+ ["git"] + command, cwd=cwd, capture_output=True, text=True, timeout=30
31
+ )
32
+ return result.returncode == 0, result.stdout.strip(), result.stderr.strip()
33
+ except (
34
+ subprocess.TimeoutExpired,
35
+ subprocess.CalledProcessError,
36
+ FileNotFoundError,
37
+ ) as e:
38
+ return False, "", str(e)
39
+
40
+ def is_git_available(self) -> bool:
41
+ """Check if git is installed and available"""
42
+ if self._git_available is None:
43
+ self._git_available = shutil.which("git") is not None
44
+ return self._git_available
45
+
46
+ def find_git_root(self, start_path: Optional[Path] = None) -> Optional[Path]:
47
+ """Find the root directory of the git repository"""
48
+ if not self.is_git_available():
49
+ return None
50
+
51
+ if start_path is None:
52
+ start_path = self.working_directory
53
+
54
+ try:
55
+ success, stdout, _ = self._run_git_command(
56
+ ["rev-parse", "--show-toplevel"], cwd=start_path
57
+ )
58
+ if success and stdout:
59
+ return Path(stdout.strip()).resolve()
60
+ except Exception:
61
+ return None
62
+
63
+ def is_git_repository(self) -> bool:
64
+ """Check if current directory is a git repository"""
65
+ if not self.is_git_available():
66
+ return False
67
+
68
+ success, _, _ = self._run_git_command(["rev-parse", "--git-dir"])
69
+ return success
70
+
71
+ def get_current_branch(self) -> Optional[str]:
72
+ """Get the current branch name"""
73
+ if not self.is_git_repository():
74
+ return None
75
+
76
+ success, stdout, _ = self._run_git_command(["branch", "--show-current"])
77
+ if success and stdout:
78
+ return stdout
79
+
80
+ success, stdout, _ = self._run_git_command(["symbolic-ref", "--short", "HEAD"])
81
+ if success and stdout:
82
+ return stdout
83
+
84
+ return None
85
+
86
+ def get_all_branches(self) -> List[str]:
87
+ """Get all local branches"""
88
+ if not self.is_git_repository():
89
+ return []
90
+
91
+ success, stdout, _ = self._run_git_command(
92
+ ["branch", "--format=%(refname:short)"]
93
+ )
94
+ if success and stdout:
95
+ return [branch.strip() for branch in stdout.split("\n") if branch.strip()]
96
+
97
+ # Fallback for repositories without commits: check if HEAD points to a branch
98
+ # This handles the case where a repository was initialized but no commits were made yet
99
+ head_success, head_output, _ = self._run_git_command(
100
+ ["symbolic-ref", "--short", "HEAD"]
101
+ )
102
+ if head_success and head_output.strip():
103
+ return [head_output.strip()]
104
+
105
+ return []
106
+
107
+ def get_last_commit(self) -> Optional[GitCommit]:
108
+ """Get information about the last commit"""
109
+ commits = self.get_commit_history(limit=1)
110
+ return commits[0] if commits else None
111
+
112
+ def get_commit_history(
113
+ self, limit: int = 10, offset: int = 0, branch: Optional[str] = None
114
+ ) -> List[GitCommit]:
115
+ """Get commit history with pagination"""
116
+ if not self.is_git_repository():
117
+ return []
118
+
119
+ if branch:
120
+ git_args = ["log", branch]
121
+ else:
122
+ git_args = ["log", "--all"]
123
+
124
+ git_args.extend(
125
+ [
126
+ f"--skip={offset}",
127
+ f"--max-count={limit}",
128
+ "--format=%H|%s|%an|%ad",
129
+ "--date=iso",
130
+ ]
131
+ )
132
+
133
+ success, stdout, _ = self._run_git_command(git_args)
134
+
135
+ if success and stdout:
136
+ commits = []
137
+ for line in stdout.strip().split("\n"):
138
+ if line.strip():
139
+ parts = line.split("|", 3)
140
+ if len(parts) == 4:
141
+ commits.append(
142
+ GitCommit(
143
+ hash=parts[0],
144
+ message=parts[1],
145
+ author=parts[2],
146
+ date=parts[3],
147
+ )
148
+ )
149
+ return commits
150
+ return []
151
+
152
+ def get_changed_files(self) -> List[str]:
153
+ """Get list of changed files"""
154
+ if not self.is_git_repository():
155
+ return []
156
+
157
+ success, stdout, _ = self._run_git_command(["status", "--porcelain"])
158
+ if success and stdout:
159
+ files = []
160
+ for line in stdout.split("\n"):
161
+ if line.strip():
162
+ filename = line[2:].lstrip() if len(line) > 2 else ""
163
+ if filename:
164
+ files.append(filename)
165
+ return files
166
+ return []
167
+
168
+ def get_changed_files_with_status(self) -> List[GitFileChange]:
169
+ """Get list of changed files with their git status"""
170
+ if not self.is_git_repository():
171
+ return []
172
+
173
+ success, stdout, _ = self._run_git_command(["status", "--porcelain"])
174
+ if success and stdout:
175
+ files = []
176
+ for line in stdout.split("\n"):
177
+ if line.strip():
178
+ if len(line) < 3:
179
+ continue
180
+
181
+ status_code = line[:2]
182
+ filename = line[2:].lstrip() if len(line) > 2 else ""
183
+
184
+ if not filename:
185
+ continue
186
+
187
+ if status_code[0] == "A" or status_code[1] == "A":
188
+ status = "added"
189
+ elif status_code[0] == "D" or status_code[1] == "D":
190
+ status = "deleted"
191
+ elif status_code[0] == "M" or status_code[1] == "M":
192
+ status = "modified"
193
+ elif status_code[0] == "R" or status_code[1] == "R":
194
+ status = "renamed"
195
+ elif status_code[0] == "?" and status_code[1] == "?":
196
+ status = "untracked"
197
+ else:
198
+ status = "modified"
199
+
200
+ files.append(
201
+ GitFileChange(
202
+ path=filename, status=status, status_code=status_code
203
+ )
204
+ )
205
+ return files
206
+ return []
207
+
208
+ def has_uncommitted_changes(self) -> bool:
209
+ """Check if there are uncommitted changes"""
210
+ return len(self.get_changed_files()) > 0
211
+
212
+ def init_repository(self) -> bool:
213
+ """Initialize a new git repository in the working directory"""
214
+ if not self.is_git_available():
215
+ return False
216
+
217
+ # Initialize repository with main as default branch
218
+ success, _, _ = self._run_git_command(["init", "-b", "main"])
219
+ if not success:
220
+ # Fallback for older git versions that don't support -b flag
221
+ success, _, _ = self._run_git_command(["init"])
222
+ if not success:
223
+ return False
224
+ # Set main as default branch
225
+ self._run_git_command(["symbolic-ref", "HEAD", "refs/heads/main"])
226
+
227
+ self.configure_git_user(TEMP_ABSTRA_EMAIL, TEMP_ABSTRA_NAME)
228
+
229
+ add_success, _, _ = self._run_git_command(["add", "."])
230
+ if add_success:
231
+ status_success, status_output, _ = self._run_git_command(
232
+ ["status", "--porcelain", "--cached"]
233
+ )
234
+ if status_success and status_output.strip():
235
+ commit_success, _, _ = self._run_git_command(
236
+ ["commit", "-m", "First commit"]
237
+ )
238
+ if commit_success:
239
+ return True
240
+
241
+ # If no files to commit or commit failed, create an empty initial commit
242
+ # This ensures we have a branch with at least one commit
243
+ empty_commit_success, err, out = self._run_git_command(
244
+ ["commit", "--allow-empty", "-m", "First commit"]
245
+ )
246
+ print("Created empty initial commit AAAA.", empty_commit_success, err, out)
247
+ return empty_commit_success
248
+
249
+ def configure_git_user(self, fallback_email: str, fallback_name: str):
250
+ """Ensure git user is configured for commits (required in CI environments)"""
251
+ name_success, name_stdout, _ = self._run_git_command(["config", "user.name"])
252
+ if not name_success or name_stdout == TEMP_ABSTRA_NAME:
253
+ self._run_git_command(["config", "user.name", fallback_name])
254
+
255
+ email_success, email_stdout, _ = self._run_git_command(["config", "user.email"])
256
+ if not email_success or email_stdout == TEMP_ABSTRA_EMAIL:
257
+ self._run_git_command(["config", "user.email", fallback_email])
258
+
259
+ def get_repository_status(self) -> GitStatus:
260
+ """Get comprehensive repository status"""
261
+ if not self.is_git_available():
262
+ return GitStatus(available=False, git_installed=False)
263
+
264
+ git_root = self.find_git_root()
265
+ if (
266
+ git_root is not None
267
+ and git_root.resolve() != self.working_directory.resolve()
268
+ ):
269
+ return GitStatus(available=False, is_monorepo=True)
270
+
271
+ if self.is_git_repository():
272
+ git_root = self.working_directory
273
+ original_working_dir = None
274
+ elif git_root := self.find_git_root():
275
+ original_working_dir = self.working_directory
276
+ self.working_directory = git_root
277
+ elif self.init_repository():
278
+ git_root = self.working_directory
279
+ original_working_dir = None
280
+ else:
281
+ return GitStatus(available=False)
282
+
283
+ try:
284
+ branch = self.get_current_branch()
285
+ if not branch:
286
+ success, stdout, _ = self._run_git_command(["rev-parse", "HEAD"])
287
+ if success and stdout:
288
+ branch = f"detached-{stdout[:8]}"
289
+ else:
290
+ return GitStatus(available=False)
291
+
292
+ branches = self.get_all_branches()
293
+ last_commit = self.get_last_commit()
294
+ changed_files = self.get_changed_files()
295
+ changed_files_with_status = self.get_changed_files_with_status()
296
+ has_changes = len(changed_files) > 0
297
+
298
+ return GitStatus(
299
+ available=True,
300
+ branch=branch,
301
+ branches=branches,
302
+ last_commit=last_commit,
303
+ has_changes=has_changes,
304
+ changed_files=changed_files,
305
+ changed_files_with_status=changed_files_with_status,
306
+ )
307
+ finally:
308
+ if "original_working_dir" in locals() and original_working_dir is not None:
309
+ self.working_directory = original_working_dir
310
+
311
+ def checkout_branch(self, branch_name: str) -> bool:
312
+ """Switch to a different branch"""
313
+ if not self.is_git_repository():
314
+ return False
315
+
316
+ success, _, stderr = self._run_git_command(["checkout", branch_name])
317
+ if not success and "did not match any file(s) known to git" in stderr:
318
+ success, _, _ = self._run_git_command(
319
+ ["checkout", "-b", branch_name, f"origin/{branch_name}"]
320
+ )
321
+
322
+ return success
323
+
324
+ def checkout_commit(self, commit_hash: str) -> bool:
325
+ """Switch to a specific commit (detached HEAD state)"""
326
+ if not self.is_git_repository():
327
+ return False
328
+
329
+ success, _, _ = self._run_git_command(["checkout", commit_hash])
330
+ return success
331
+
332
+ def pull_changes(
333
+ self,
334
+ strategy: str = "merge",
335
+ allow_unrelated: bool = True,
336
+ conflict_resolution: Optional[str] = "theirs",
337
+ ) -> bool:
338
+ """Pull changes from abstra remote repository
339
+
340
+ Args:
341
+ strategy: How to reconcile divergent branches. Options:
342
+ - "merge": Create a merge commit (git pull --no-rebase)
343
+ - "rebase": Rebase local commits on top of remote (git pull --rebase)
344
+ - "ff-only": Only allow fast-forward merges (git pull --ff-only)
345
+ allow_unrelated: Allow merging unrelated histories (git pull --allow-unrelated-histories)
346
+ conflict_resolution: How to resolve conflicts automatically. Options:
347
+ - "theirs": Accept all incoming changes (git pull -X theirs)
348
+ - "ours": Keep all local changes (git pull -X ours)
349
+ - None: Handle conflicts manually (default)
350
+ """
351
+ if not self.is_git_repository():
352
+ return False
353
+
354
+ pull_cmd = ["pull"]
355
+
356
+ if strategy == "merge":
357
+ pull_cmd.append("--no-rebase")
358
+ elif strategy == "rebase":
359
+ pull_cmd.append("--rebase")
360
+ elif strategy == "ff-only":
361
+ pull_cmd.append("--ff-only")
362
+ else:
363
+ pull_cmd.append("--no-rebase")
364
+
365
+ if allow_unrelated:
366
+ pull_cmd.append("--allow-unrelated-histories")
367
+
368
+ if conflict_resolution == "theirs":
369
+ pull_cmd.extend(["-X", "theirs"])
370
+ elif conflict_resolution == "ours":
371
+ pull_cmd.extend(["-X", "ours"])
372
+
373
+ pull_cmd.extend([REMOTE_NAME, "main"])
374
+
375
+ success, _, e = self._run_git_command(pull_cmd)
376
+ if not success:
377
+ print("There was an error during git pull:", e)
378
+
379
+ return success
380
+
381
+ def commit_changes(self, message: str, add_all: bool = True) -> bool:
382
+ """Commit changes with a message"""
383
+ if not self.is_git_repository():
384
+ return False
385
+
386
+ if add_all:
387
+ success, _, _ = self._run_git_command(["add", "."])
388
+ if not success:
389
+ return False
390
+
391
+ success, _, _ = self._run_git_command(["commit", "-m", message])
392
+ return success
393
+
394
+ def stash_changes(self, message: str = "WIP") -> bool:
395
+ """Stash uncommitted changes"""
396
+ if not self.is_git_repository():
397
+ return False
398
+
399
+ success, _, _ = self._run_git_command(
400
+ ["stash", "push", "-m", message, "--include-untracked"]
401
+ )
402
+ return success
403
+
404
+ def get_remotes(self) -> List[str]:
405
+ """Get list of remote names"""
406
+ if not self.is_git_repository():
407
+ return []
408
+
409
+ success, stdout, _ = self._run_git_command(["remote"])
410
+ if success and stdout:
411
+ return [remote.strip() for remote in stdout.split("\n") if remote.strip()]
412
+ return []
413
+
414
+ def has_remote(self, remote_name: str) -> bool:
415
+ """Check if a remote exists"""
416
+ return remote_name in self.get_remotes()
417
+
418
+ def add_remote(self, remote_name: str, remote_url: str) -> bool:
419
+ """Add a remote to the repository"""
420
+ if not self.is_git_repository():
421
+ return False
422
+
423
+ success, _, _ = self._run_git_command(
424
+ ["remote", "add", remote_name, remote_url]
425
+ )
426
+ return success
427
+
428
+ def set_remote_url(self, remote_name: str, remote_url: str) -> bool:
429
+ """Set/update the URL for a remote"""
430
+ if not self.is_git_repository():
431
+ return False
432
+
433
+ success, _, _ = self._run_git_command(
434
+ ["remote", "set-url", remote_name, remote_url]
435
+ )
436
+ return success
437
+
438
+ def set_git_config(self, key: str, value: str, local: bool = True) -> bool:
439
+ """Set a git configuration value"""
440
+ if not self.is_git_repository():
441
+ return False
442
+
443
+ config_flag = "--local" if local else "--global"
444
+ success, _, _ = self._run_git_command(["config", config_flag, key, value])
445
+ return success
446
+
447
+ def get_git_config(self, key: str, local: bool = True) -> Optional[str]:
448
+ """Get a git configuration value"""
449
+ if not self.is_git_repository():
450
+ return None
451
+
452
+ config_flag = "--local" if local else "--global"
453
+ success, stdout, _ = self._run_git_command(["config", config_flag, key])
454
+ return stdout.strip() if success and stdout else None
455
+
456
+ def get_ahead_behind_count(
457
+ self, local_commit: str, remote_commit: str
458
+ ) -> Tuple[int, int]:
459
+ if not self.is_git_repository():
460
+ return (0, 0)
461
+
462
+ remote_exists_success, _, _ = self._run_git_command(
463
+ ["cat-file", "-e", remote_commit]
464
+ )
465
+
466
+ # If remote commit doesn't exist locally, we need to fetch first (we're behind)
467
+ if not remote_exists_success:
468
+ # We can't calculate exact counts without fetching, but we know:
469
+ # - We have local changes (since commits are different)
470
+ # - Remote has changes we don't have (since remote commit doesn't exist locally)
471
+ # This indicates divergent branches - both ahead and behind
472
+ return (1, 1) # Simplified: indicates divergence requiring merge/rebase
473
+
474
+ # If remote commit exists locally, we can calculate normal ahead/behind
475
+ # Count commits that local has but remote doesn't (ahead)
476
+ ahead_success, ahead_stdout, _ = self._run_git_command(
477
+ ["rev-list", "--count", f"{remote_commit}..{local_commit}"]
478
+ )
479
+ ahead_count = (
480
+ int(ahead_stdout.strip()) if ahead_success and ahead_stdout.strip() else 0
481
+ )
482
+
483
+ # Count commits that remote has but local doesn't (behind)
484
+ behind_success, behind_stdout, _ = self._run_git_command(
485
+ ["rev-list", "--count", f"{local_commit}..{remote_commit}"]
486
+ )
487
+ behind_count = (
488
+ int(behind_stdout.strip())
489
+ if behind_success and behind_stdout.strip()
490
+ else 0
491
+ )
492
+
493
+ return (ahead_count, behind_count)
494
+
495
+ def push_and_deploy(self, branch: str = "main") -> bool:
496
+ """Deploy to Abstra remote (push to abstra remote)"""
497
+ if not self.is_git_repository():
498
+ return False
499
+
500
+ if not self.has_remote(REMOTE_NAME):
501
+ return False
502
+
503
+ success, _, _ = self._run_git_command(["push", REMOTE_NAME, branch])
504
+
505
+ return success
506
+
507
+ def revert_commit(self, commit_hash: str) -> bool:
508
+ """Reset working directory to match a previous commit and create a new commit with that content"""
509
+ if not self.is_git_repository():
510
+ return False
511
+
512
+ if not commit_hash.strip():
513
+ return False
514
+
515
+ if self.has_uncommitted_changes():
516
+ return False
517
+
518
+ try:
519
+ # Step 1: Reset the working directory and index to match the target commit
520
+ # This will replace all files with their state from the target commit
521
+ success_reset, _, _ = self._run_git_command(
522
+ ["reset", "--hard", commit_hash]
523
+ )
524
+ if not success_reset:
525
+ return False
526
+
527
+ # Step 2: Reset HEAD back to the original position (soft reset)
528
+ # This keeps the files from target commit but moves HEAD back
529
+ success_soft, _, _ = self._run_git_command(["reset", "--soft", "HEAD@{1}"])
530
+ if not success_soft:
531
+ return False
532
+
533
+ # Step 3: Create a new commit with the restored content
534
+ commit_message = f"Restore content from commit {commit_hash[:8]}"
535
+ success_commit, _, _ = self._run_git_command(
536
+ ["commit", "-m", commit_message]
537
+ )
538
+
539
+ return success_commit
540
+
541
+ except Exception:
542
+ self._run_git_command(["reset", "--hard", "HEAD"])
543
+ return False
544
+
545
+ def check_merge_conflicts(self, remote_commit: str) -> bool:
546
+ """Check if merging with remote commit would cause conflicts"""
547
+ if not self.is_git_repository():
548
+ return False
549
+
550
+ if not remote_commit:
551
+ return False
552
+
553
+ # Get current commit
554
+ success, current_commit, _ = self._run_git_command(["rev-parse", "HEAD"])
555
+ if not success:
556
+ return False
557
+
558
+ # Use git merge-tree to detect potential conflicts without actually merging
559
+ # git merge-tree <base> <branch1> <branch2>
560
+ # We need to find the merge base first
561
+ success, merge_base, _ = self._run_git_command(
562
+ ["merge-base", current_commit.strip(), remote_commit]
563
+ )
564
+ if not success:
565
+ # If no merge base, assume potential conflicts
566
+ return True
567
+
568
+ # Check if merge would have conflicts
569
+ success, output, _ = self._run_git_command(
570
+ ["merge-tree", merge_base.strip(), current_commit.strip(), remote_commit]
571
+ )
572
+
573
+ # If merge-tree succeeds and output contains conflict markers, there are conflicts
574
+ if success and output:
575
+ # Look for conflict markers in the output
576
+ return "<<<<<<< " in output or "=======" in output or ">>>>>>> " in output
577
+
578
+ return False