abstra 3.23.11__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.
- abstra/ai.py +2 -0
- abstra/cli.py +8 -3
- {abstra-3.23.11.dist-info → abstra-3.24.0.dist-info}/METADATA +2 -1
- {abstra-3.23.11.dist-info → abstra-3.24.0.dist-info}/RECORD +201 -191
- abstra_internals/consts/filepaths.py +1 -1
- abstra_internals/contracts_generated.py +5382 -5233
- abstra_internals/controllers/ai.py +23 -15
- abstra_internals/controllers/git.py +305 -0
- abstra_internals/controllers/main.py +10 -6
- abstra_internals/entities/forms/template.py +2 -2
- abstra_internals/environment.py +6 -0
- abstra_internals/interface/cli/deploy.py +1 -1
- abstra_internals/interface/sdk/ai.py +157 -19
- abstra_internals/repositories/git/__init__.py +38 -0
- abstra_internals/repositories/git/dulwich.py +1353 -0
- abstra_internals/repositories/git/git_test.py +1572 -0
- abstra_internals/repositories/git/native.py +578 -0
- abstra_internals/repositories/git/types.py +267 -0
- abstra_internals/repositories/linter/rules/env_in_bundle.py +5 -5
- abstra_internals/repositories/linter/rules/env_in_bundle_test.py +6 -6
- abstra_internals/repositories/linter/rules/venv_in_bundle.py +9 -16
- abstra_internals/server/blueprints/editor.py +4 -0
- abstra_internals/server/routes/ai.py +1 -18
- abstra_internals/server/routes/git.py +190 -0
- abstra_internals/server/routes/workspace.py +1 -1
- abstra_internals/services/file_watcher.py +32 -13
- abstra_internals/services/fs.py +8 -5
- abstra_internals/services/fs_test.py +4 -6
- abstra_internals/templates/__init__.py +0 -11
- abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.eb8ccb64.js +2 -0
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.9f98292e.js +2 -0
- abstra_statics/dist/assets/ApiKeys.e975c4f3.js +2 -0
- abstra_statics/dist/assets/App.f62faff6.js +2 -0
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.5aa45ac1.js +2 -0
- abstra_statics/dist/assets/BaseLayout.1ec2c96d.js +2 -0
- abstra_statics/dist/assets/Billing.60adc9fa.js +2 -0
- abstra_statics/dist/assets/{Breadcrumb.f11a16d0.js → Breadcrumb.26dec5f7.js} +2 -2
- abstra_statics/dist/assets/{Builds.2bf5dad8.js → Builds.f86210bc.js} +2 -2
- abstra_statics/dist/assets/{Card.6caecaa4.js → Card.6cfffb80.js} +2 -2
- abstra_statics/dist/assets/{CircularLoading.bc88c029.js → CircularLoading.a13d6a76.js} +2 -2
- abstra_statics/dist/assets/CloseCircleOutlined.30bc25a8.js +2 -0
- abstra_statics/dist/assets/{ConnectorsView.aeb00ce8.css → ConnectorsView.33c5380f.css} +1 -1
- abstra_statics/dist/assets/ConnectorsView.eb4c769f.js +2 -0
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.8d2e4672.js +2 -0
- abstra_statics/dist/assets/ContentLayout.d03fee5b.js +2 -0
- abstra_statics/dist/assets/{CrudView.79588134.js → CrudView.c0824225.js} +2 -2
- abstra_statics/dist/assets/{DocsButton.vue_vue_type_script_setup_true_lang.6566ff97.js → DocsButton.vue_vue_type_script_setup_true_lang.3668aee4.js} +2 -2
- abstra_statics/dist/assets/{EditorLogin.e704b2ee.js → EditorLogin.46db248f.js} +2 -2
- abstra_statics/dist/assets/EditorsView.e72621fa.js +2 -0
- abstra_statics/dist/assets/EnvVars.f9f9d61f.js +2 -0
- abstra_statics/dist/assets/{Error.f378696f.js → Error.864f05b3.js} +2 -2
- abstra_statics/dist/assets/ExclamationCircleOutlined.a489b996.js +2 -0
- abstra_statics/dist/assets/{Files.06a52abd.js → Files.afe615e1.js} +2 -2
- abstra_statics/dist/assets/Form.556d0de2.js +2 -0
- abstra_statics/dist/assets/{FormRunner.2b75fd4d.js → FormRunner.7f56a8c6.js} +2 -2
- abstra_statics/dist/assets/Home.287d17f8.js +2 -0
- abstra_statics/dist/assets/Home.5b7e9c23.js +2 -0
- abstra_statics/dist/assets/{Live.08670b33.js → Live.50bacfea.js} +2 -2
- abstra_statics/dist/assets/LoadingContainer.ebace8de.js +2 -0
- abstra_statics/dist/assets/{LoadingOutlined.659d6aae.js → LoadingOutlined.9e949112.js} +2 -2
- abstra_statics/dist/assets/{Login.8491c4b7.js → Login.536a3067.js} +2 -2
- abstra_statics/dist/assets/Login.bec408c9.js +2 -0
- abstra_statics/dist/assets/{Login.vue_vue_type_script_setup_true_lang.eb94ecc3.js → Login.vue_vue_type_script_setup_true_lang.1c3f108d.js} +2 -2
- abstra_statics/dist/assets/{Logo.a32d36e7.js → Logo.82d6ab70.js} +2 -2
- abstra_statics/dist/assets/{Logs.030c8a19.js → Logs.f6135084.js} +2 -2
- abstra_statics/dist/assets/{LogsController.7e2c0c7a.js → LogsController.6b666816.js} +2 -2
- abstra_statics/dist/assets/Main.77b115f8.js +2 -0
- abstra_statics/dist/assets/{MockForm.8b56fd25.js → MockForm.deda9355.js} +2 -2
- abstra_statics/dist/assets/Navbar.4a6f2b09.js +2 -0
- abstra_statics/dist/assets/NewEditor.5f84de86.css +1 -0
- abstra_statics/dist/assets/NewEditor.e558e47d.js +8 -0
- abstra_statics/dist/assets/OidcLoginCallback.7f514b45.js +2 -0
- abstra_statics/dist/assets/OidcLogoutCallback.038813a1.js +2 -0
- abstra_statics/dist/assets/OmniChat.05ba8d8a.css +1 -0
- abstra_statics/dist/assets/OmniChat.60d98deb.js +6 -0
- abstra_statics/dist/assets/{OnboardingView.262e82cc.js → OnboardingView.9413ee50.js} +2 -2
- abstra_statics/dist/assets/{Organization.4f3ccb55.js → Organization.7203cc0b.js} +2 -2
- abstra_statics/dist/assets/Organizations.91220ca0.js +2 -0
- abstra_statics/dist/assets/{PhArrowCounterClockwise.vue.a1a917b9.js → PhArrowCounterClockwise.vue.8090d021.js} +2 -2
- abstra_statics/dist/assets/{PhArrowSquareOut.vue.eb4455ba.js → PhArrowSquareOut.vue.26582195.js} +2 -2
- abstra_statics/dist/assets/{PhBookBookmark.vue.7bb66888.js → PhBookBookmark.vue.5b7ab079.js} +2 -2
- abstra_statics/dist/assets/{PhChats.vue.88d84608.js → PhChats.vue.b5df7174.js} +2 -2
- abstra_statics/dist/assets/{PhClockCounterClockwise.vue.02d85ea5.js → PhClockCounterClockwise.vue.812311ad.js} +2 -2
- abstra_statics/dist/assets/{PhCopy.vue.5664181e.js → PhCopy.vue.59b0f1b4.js} +2 -2
- abstra_statics/dist/assets/{PhCopySimple.vue.beb69d1f.js → PhCopySimple.vue.d41d9160.js} +2 -2
- abstra_statics/dist/assets/{PhCube.vue.8de1132a.js → PhCube.vue.63ae7d32.js} +2 -2
- abstra_statics/dist/assets/PhDatabase.vue.edfcb96b.js +2 -0
- abstra_statics/dist/assets/{PhDotsThreeVertical.vue.9e306480.js → PhDotsThreeVertical.vue.ab4580a5.js} +2 -2
- abstra_statics/dist/assets/PhDownloadSimple.vue.c2eaaad1.js +2 -0
- abstra_statics/dist/assets/{PhFolderPlus.vue.658105d4.js → PhFolderPlus.vue.05ba4a5c.js} +2 -2
- abstra_statics/dist/assets/{PhGear.vue.68bce043.js → PhGear.vue.0e4a6135.js} +2 -2
- abstra_statics/dist/assets/{PhKey.vue.3f5192b6.js → PhKey.vue.b2c184d1.js} +2 -2
- abstra_statics/dist/assets/{PhPencil.vue.ee28fb6e.js → PhPencil.vue.2f2fe576.js} +2 -2
- abstra_statics/dist/assets/{PhPencilSimple.vue.148807f6.js → PhPencilSimple.vue.cc8620ae.js} +2 -2
- abstra_statics/dist/assets/{PhRocket.vue.7ba12859.js → PhRocket.vue.e397203c.js} +2 -2
- abstra_statics/dist/assets/{PhSignOut.vue.aa78b6eb.js → PhSignOut.vue.a271f14b.js} +2 -2
- abstra_statics/dist/assets/{PhSparkle.vue.7820be45.js → PhSparkle.vue.726defa2.js} +2 -2
- abstra_statics/dist/assets/{PhUserList.vue.04f21cbe.js → PhUserList.vue.6a29f16d.js} +2 -2
- abstra_statics/dist/assets/{PhUsersThree.vue.8b11b97d.js → PhUsersThree.vue.275d13ab.js} +2 -2
- abstra_statics/dist/assets/PhWarningCircle.vue.3b1aca1b.js +2 -0
- abstra_statics/dist/assets/{PhWebhooksLogo.vue.9e20d559.js → PhWebhooksLogo.vue.5e772aac.js} +2 -2
- abstra_statics/dist/assets/{PlayerConfigProvider.0915b29f.js → PlayerConfigProvider.e90a2b41.js} +2 -2
- abstra_statics/dist/assets/{PlayerNavbar.78cf73f2.js → PlayerNavbar.11ec1844.js} +2 -2
- abstra_statics/dist/assets/Project.c16740fb.js +2 -0
- abstra_statics/dist/assets/ProjectLogin.e7a6f444.js +2 -0
- abstra_statics/dist/assets/{ProjectSettings.f9bf9124.js → ProjectSettings.52c19693.js} +2 -2
- abstra_statics/dist/assets/{ProjectsView.407ea382.js → ProjectsView.22fa7a8e.js} +2 -2
- abstra_statics/dist/assets/{SaveButton.e70cd09e.js → SaveButton.719393d2.js} +2 -2
- abstra_statics/dist/assets/{ScrollArea.vue_vue_type_script_setup_true_lang.cf352882.js → ScrollArea.vue_vue_type_script_setup_true_lang.d4028954.js} +2 -2
- abstra_statics/dist/assets/{Sidebar.e69f49bd.css → Sidebar.29baeab0.css} +1 -1
- abstra_statics/dist/assets/{Sidebar.ce129494.js → Sidebar.5cb8e04e.js} +2 -2
- abstra_statics/dist/assets/Sql.23d80bad.js +5 -0
- abstra_statics/dist/assets/Sql.90e6e2ba.css +1 -0
- abstra_statics/dist/assets/Steps.8e5d201a.js +2 -0
- abstra_statics/dist/assets/TableCard.529112b9.css +1 -0
- abstra_statics/dist/assets/TableCard.59f95f8f.js +2 -0
- abstra_statics/dist/assets/TableEditor.5853a363.css +1 -0
- abstra_statics/dist/assets/TableEditor.8539f984.js +2 -0
- abstra_statics/dist/assets/Tables.44d953f7.js +2 -0
- abstra_statics/dist/assets/TablesDiagram.8e47383c.js +15 -0
- abstra_statics/dist/assets/TablesDiagram.a588e7ff.css +1 -0
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.6866fb32.js +2 -0
- abstra_statics/dist/assets/{Tasks.c9ba3095.js → Tasks.09551b19.js} +2 -2
- abstra_statics/dist/assets/{UploadOutlined.c563b054.js → UploadOutlined.eab75eb0.js} +2 -2
- abstra_statics/dist/assets/View.5fd7ddf0.js +2 -0
- abstra_statics/dist/assets/{View.vue_vue_type_script_setup_true_lang.a00f21c8.js → View.vue_vue_type_script_setup_true_lang.a904f400.js} +2 -2
- abstra_statics/dist/assets/{Watermark.c71c105b.js → Watermark.ab3d818f.js} +2 -2
- abstra_statics/dist/assets/WebEditor.d6ec6392.js +2 -0
- abstra_statics/dist/assets/{WidgetPreview.8bd406f7.js → WidgetPreview.86b31dec.js} +2 -2
- abstra_statics/dist/assets/{ant-design.06b818ab.js → ant-design.a865486e.js} +2 -2
- abstra_statics/dist/assets/apiKey.72f497ca.js +2 -0
- abstra_statics/dist/assets/{asyncComputed.bfb7341c.js → asyncComputed.cf5282fc.js} +2 -2
- abstra_statics/dist/assets/{build.aef51d48.js → build.df2d55cc.js} +2 -2
- abstra_statics/dist/assets/colorHelpers.37d9932b.js +2 -0
- abstra_statics/dist/assets/console.2bf7f04d.js +17 -0
- abstra_statics/dist/assets/{constants.b75d9567.js → constants.7d38ec8b.js} +2 -2
- abstra_statics/dist/assets/contracts.generated.590b1102.js +2 -0
- abstra_statics/dist/assets/{cssMode.0aa24dcd.js → cssMode.7133c7cb.js} +2 -2
- abstra_statics/dist/assets/{datetime.46141396.js → datetime.8de2ff28.js} +2 -2
- abstra_statics/dist/assets/dayjs.f18bbbca.js +2 -0
- abstra_statics/dist/assets/editor.3a4714e3.js +2 -0
- abstra_statics/dist/assets/editor.main.9c635b9a.js +2 -0
- abstra_statics/dist/assets/fetch.89fd5b7b.js +2 -0
- abstra_statics/dist/assets/{folder.fe6adcd2.js → folder.d8e23009.js} +2 -2
- abstra_statics/dist/assets/{freemarker2.8bd96640.js → freemarker2.6698d1ea.js} +2 -2
- abstra_statics/dist/assets/{handlebars.e5dd9b19.js → handlebars.a6c42dc0.js} +2 -2
- abstra_statics/dist/assets/{html.7bcef916.js → html.493a5410.js} +2 -2
- abstra_statics/dist/assets/{htmlMode.ce91c230.js → htmlMode.a28b2fca.js} +2 -2
- abstra_statics/dist/assets/{index.fe015dd3.js → index.2af3391c.js} +2 -2
- abstra_statics/dist/assets/{index.57a61691.js → index.4176fe88.js} +2 -2
- abstra_statics/dist/assets/{index.16664500.js → index.4d20c159.js} +2 -2
- abstra_statics/dist/assets/{index.ee218f7a.js → index.5d6b1e62.js} +2 -2
- abstra_statics/dist/assets/{index.06cad1f2.js → index.79ce3bf1.js} +2 -2
- abstra_statics/dist/assets/{index.161ccee5.js → index.c34a405a.js} +2 -2
- abstra_statics/dist/assets/index.fb182bd1.js +2 -0
- abstra_statics/dist/assets/{index.005bab7f.js → index.fb49354b.js} +2 -2
- abstra_statics/dist/assets/{javascript.4d5c0a37.js → javascript.3a36cf17.js} +2 -2
- abstra_statics/dist/assets/{jsonMode.0942a255.js → jsonMode.bead6ac8.js} +2 -2
- abstra_statics/dist/assets/{jwt-decode.esm.e1e8e181.js → jwt-decode.esm.d4517a10.js} +8 -8
- abstra_statics/dist/assets/{linters.39cc198f.js → linters.2f3141cb.js} +2 -2
- abstra_statics/dist/assets/{liquid.884b015e.js → liquid.0c337fae.js} +3 -3
- abstra_statics/dist/assets/member.48d6f2cd.js +2 -0
- abstra_statics/dist/assets/{metadata.034775c8.js → metadata.c3aed6e1.js} +2 -2
- abstra_statics/dist/assets/omniChatStore.c53bcca2.js +8 -0
- abstra_statics/dist/assets/{organization.a29009bc.js → organization.928c9bef.js} +2 -2
- abstra_statics/dist/assets/player.d3aeafc5.js +2 -0
- abstra_statics/dist/assets/{plotly.min.df1e817c.js → plotly.min.7225d3a0.js} +2 -2
- abstra_statics/dist/assets/{polling.84bf96db.js → polling.82ee6b45.js} +2 -2
- abstra_statics/dist/assets/{project.a9ad86e9.js → project.619b7244.js} +2 -2
- abstra_statics/dist/assets/{python.17c791bf.js → python.05764499.js} +3 -3
- abstra_statics/dist/assets/{razor.4b071c3b.js → razor.81a45581.js} +2 -2
- abstra_statics/dist/assets/record.7f43486c.js +2 -0
- abstra_statics/dist/assets/{redirect.cd158e43.js → redirect.f028a879.js} +2 -2
- abstra_statics/dist/assets/repository.9534db4b.js +2 -0
- abstra_statics/dist/assets/repository.c15239ce.js +2 -0
- abstra_statics/dist/assets/router.262190ec.js +2 -0
- abstra_statics/dist/assets/router.424f7da9.js +18 -0
- abstra_statics/dist/assets/string.0acf5572.js +2 -0
- abstra_statics/dist/assets/{tables.928d4c4a.js → tables.1f68ec62.js} +2 -2
- abstra_statics/dist/assets/{tasksController.95334db5.js → tasksController.371896de.js} +2 -2
- abstra_statics/dist/assets/{toggleHighContrast.67a76682.js → toggleHighContrast.0d0e5662.js} +7 -7
- abstra_statics/dist/assets/{tsMode.bd4befb2.js → tsMode.6eadbf06.js} +2 -2
- abstra_statics/dist/assets/{typescript.b5722de7.js → typescript.1670e287.js} +2 -2
- abstra_statics/dist/assets/url.e8732f77.js +2 -0
- abstra_statics/dist/assets/{useCodebaseEvents.47e4fcfe.js → useCodebaseEvents.53dec1f2.js} +2 -2
- abstra_statics/dist/assets/useTables.4f034cf8.js +2 -0
- abstra_statics/dist/assets/userStore.31024da3.js +2 -0
- abstra_statics/dist/assets/uuid.bde15ce7.js +2 -0
- abstra_statics/dist/assets/vue-flow-background.818c7852.js +2 -0
- abstra_statics/dist/assets/vue-flow-core.1180ec83.js +22 -0
- abstra_statics/dist/assets/{vue-quill.esm-bundler.d6a7c536.js → vue-quill.esm-bundler.c4f04985.js} +2 -2
- abstra_statics/dist/assets/{workspaceStore.cca10750.js → workspaceStore.6244d03d.js} +2 -2
- abstra_statics/dist/assets/{xml.0a62ffce.js → xml.f2867af8.js} +3 -3
- abstra_statics/dist/assets/{yaml.1efc213f.js → yaml.5427bb1b.js} +3 -3
- abstra_statics/dist/console.html +14 -15
- abstra_statics/dist/editor.html +13 -13
- abstra_statics/dist/player.html +9 -9
- tests/e2e/test_crud_files.py +1 -0
- abstra_internals/templates/abstraignore +0 -8
- abstra_statics/dist/assets/AbstraButton.vue_vue_type_script_setup_true_lang.3fce0fbb.js +0 -2
- abstra_statics/dist/assets/AbstraLogo.vue_vue_type_script_setup_true_lang.4fb1f88a.js +0 -2
- abstra_statics/dist/assets/ApiKeys.56b97ced.js +0 -2
- abstra_statics/dist/assets/App.bd5ec032.js +0 -2
- abstra_statics/dist/assets/App.vue_vue_type_style_index_0_lang.79524273.js +0 -2
- abstra_statics/dist/assets/BaseLayout.ac22ef55.js +0 -2
- abstra_statics/dist/assets/Billing.2c39d133.js +0 -2
- abstra_statics/dist/assets/CloseCircleOutlined.12f63f77.js +0 -2
- abstra_statics/dist/assets/ConnectorsView.97a8c63b.js +0 -2
- abstra_statics/dist/assets/ConsoleOmniChat.vue_vue_type_script_setup_true_lang.48b29302.js +0 -2
- abstra_statics/dist/assets/ContentLayout.072b3790.js +0 -2
- abstra_statics/dist/assets/EditorsView.987a7eda.js +0 -2
- abstra_statics/dist/assets/EnvVars.c5e84d82.js +0 -2
- abstra_statics/dist/assets/ExclamationCircleOutlined.f9a25080.js +0 -2
- abstra_statics/dist/assets/Form.4633ab8e.js +0 -2
- abstra_statics/dist/assets/Home.a883da25.js +0 -2
- abstra_statics/dist/assets/Home.d122da63.js +0 -2
- abstra_statics/dist/assets/LoadingContainer.b11f052c.js +0 -2
- abstra_statics/dist/assets/Login.84d39327.js +0 -2
- abstra_statics/dist/assets/Main.70db8614.js +0 -2
- abstra_statics/dist/assets/Navbar.6acd9007.js +0 -2
- abstra_statics/dist/assets/NewEditor.15e29eac.js +0 -8
- abstra_statics/dist/assets/NewEditor.6dd8f03b.css +0 -1
- abstra_statics/dist/assets/OidcLoginCallback.b65a6f9f.js +0 -2
- abstra_statics/dist/assets/OidcLogoutCallback.dabd09cf.js +0 -2
- abstra_statics/dist/assets/OmniChat.078af315.css +0 -1
- abstra_statics/dist/assets/OmniChat.6ed96f8e.js +0 -6
- abstra_statics/dist/assets/Organizations.d90dd629.js +0 -2
- abstra_statics/dist/assets/PhDownloadSimple.vue.6192bb0e.js +0 -2
- abstra_statics/dist/assets/PhPencilSimpleLine.vue.af76e884.js +0 -2
- abstra_statics/dist/assets/Project.34c75b7c.js +0 -2
- abstra_statics/dist/assets/ProjectLogin.9b8cb3d1.js +0 -2
- abstra_statics/dist/assets/Sql.3cdc910a.css +0 -1
- abstra_statics/dist/assets/Sql.4e31662a.js +0 -5
- abstra_statics/dist/assets/Steps.4c850501.js +0 -2
- abstra_statics/dist/assets/TableEditor.1e680eaf.css +0 -1
- abstra_statics/dist/assets/TableEditor.e7371677.js +0 -2
- abstra_statics/dist/assets/Tables.f2c23e20.js +0 -2
- abstra_statics/dist/assets/TablesDiagram.1ec45dd9.css +0 -1
- abstra_statics/dist/assets/TablesDiagram.8297ec4b.js +0 -15
- abstra_statics/dist/assets/TablesTabs.vue_vue_type_script_setup_true_lang.ec94f9e7.js +0 -2
- abstra_statics/dist/assets/View.4c3c38ba.js +0 -2
- abstra_statics/dist/assets/WebEditor.4ef39590.js +0 -2
- abstra_statics/dist/assets/apiKey.e3aa9e16.js +0 -2
- abstra_statics/dist/assets/colorHelpers.e94da383.js +0 -2
- abstra_statics/dist/assets/console.86947afb.js +0 -25
- abstra_statics/dist/assets/contracts.generated.7508e893.js +0 -2
- abstra_statics/dist/assets/dayjs.b4dc8227.js +0 -2
- abstra_statics/dist/assets/editor.4f4b7594.js +0 -2
- abstra_statics/dist/assets/editor.main.340c0a2d.js +0 -2
- abstra_statics/dist/assets/fetch.cfa094a0.js +0 -2
- abstra_statics/dist/assets/index.a884598f.js +0 -2
- abstra_statics/dist/assets/index.eb8f01b7.js +0 -2
- abstra_statics/dist/assets/member.af05829c.js +0 -2
- abstra_statics/dist/assets/omniChatStore.19fcae1a.js +0 -8
- abstra_statics/dist/assets/player.b9e54e21.js +0 -2
- abstra_statics/dist/assets/record.06867f70.js +0 -2
- abstra_statics/dist/assets/repository.40cf87c6.js +0 -2
- abstra_statics/dist/assets/repository.ecf19994.js +0 -2
- abstra_statics/dist/assets/router.8ccbba96.js +0 -10
- abstra_statics/dist/assets/router.c00c99d7.js +0 -2
- abstra_statics/dist/assets/string.ea3b2597.js +0 -2
- abstra_statics/dist/assets/url.62ce842c.js +0 -2
- abstra_statics/dist/assets/userStore.51f1e2d0.js +0 -2
- abstra_statics/dist/assets/uuid.f4308f15.js +0 -2
- abstra_statics/dist/assets/vue-flow-background.03bf6b4b.js +0 -22
- {abstra-3.23.11.dist-info → abstra-3.24.0.dist-info}/WHEEL +0 -0
- {abstra-3.23.11.dist-info → abstra-3.24.0.dist-info}/entry_points.txt +0 -0
- {abstra-3.23.11.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
|
|
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
|
|
14
|
-
label = "Add env to
|
|
13
|
+
class AddEnvToGitIgnore(LinterFix):
|
|
14
|
+
label = "Add env to git ignore"
|
|
15
15
|
|
|
16
16
|
def fix(self):
|
|
17
|
-
abstraignore_file = Settings.root_path /
|
|
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 = [
|
|
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
|
|
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 /
|
|
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
|
|
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
|
|
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 /
|
|
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 /
|
|
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
|
|
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
|
|
34
|
-
label = "Add virtual env to
|
|
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 /
|
|
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 = [
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
|
|
@@ -3,7 +3,7 @@ import flask
|
|
|
3
3
|
from abstra_internals.contracts_generated import AbstraLibApiAiStreamRequest
|
|
4
4
|
from abstra_internals.controllers.ai import AiController
|
|
5
5
|
from abstra_internals.controllers.main import MainController
|
|
6
|
-
from abstra_internals.usage import
|
|
6
|
+
from abstra_internals.usage import editor_usage
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def get_editor_bp(main_controller: MainController):
|
|
@@ -67,23 +67,6 @@ def get_editor_bp(main_controller: MainController):
|
|
|
67
67
|
controller.delete_thread(thread_id)
|
|
68
68
|
return {"success": True}
|
|
69
69
|
|
|
70
|
-
@bp.post("/vote")
|
|
71
|
-
def _vote():
|
|
72
|
-
body = flask.request.json
|
|
73
|
-
if not body:
|
|
74
|
-
flask.abort(400)
|
|
75
|
-
vote = body.get("vote")
|
|
76
|
-
question = body.get("question")
|
|
77
|
-
answer = body.get("answer")
|
|
78
|
-
context = body.get("context")
|
|
79
|
-
|
|
80
|
-
editor_manual_usage(
|
|
81
|
-
event="ai_vote",
|
|
82
|
-
payload=dict(vote=vote, question=question, answer=answer, context=context),
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
return {"success": True}
|
|
86
|
-
|
|
87
70
|
@bp.post("/start-conversation")
|
|
88
71
|
def _start_conversation():
|
|
89
72
|
"""
|
|
@@ -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
|