automagik-forge 0.1.13 → 0.1.14
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.
- package/README.md +143 -447
- package/dist/linux-x64/automagik-forge-mcp.zip +0 -0
- package/{npx-cli/automagik-forge-0.0.55.tgz → dist/linux-x64/automagik-forge.zip} +0 -0
- package/package.json +13 -23
- package/.cargo/config.toml +0 -13
- package/.claude/commands/commit.md +0 -376
- package/.claude/commands/prompt.md +0 -871
- package/.env.example +0 -20
- package/.github/actions/setup-node/action.yml +0 -29
- package/.github/images/automagik-logo.png +0 -0
- package/.github/workflows/pre-release.yml +0 -470
- package/.github/workflows/publish.yml +0 -145
- package/.github/workflows/test.yml +0 -63
- package/.mcp.json +0 -57
- package/AGENT.md +0 -40
- package/CLAUDE.md +0 -40
- package/CODE-OF-CONDUCT.md +0 -89
- package/Cargo.toml +0 -19
- package/Dockerfile +0 -43
- package/LICENSE +0 -201
- package/Makefile +0 -97
- package/backend/.sqlx/query-01b7e2bac1261d8be3d03c03df3e5220590da6c31c77f161074fc62752d63881.json +0 -12
- package/backend/.sqlx/query-03f2b02ba6dc5ea2b3cf6b1004caea0ad6bcc10ebd63f441d321a389f026e263.json +0 -12
- package/backend/.sqlx/query-0923b77d137a29fc54d399a873ff15fc4af894490bc65a4d344a7575cb0d8643.json +0 -12
- package/backend/.sqlx/query-0f808bcdb63c5f180836e448dd64c435c51758b2fc54a52ce9e67495b1ab200e.json +0 -68
- package/backend/.sqlx/query-1268afe9ca849daa6722e3df7ca8e9e61f0d37052e782bb5452ab8e1018d9b63.json +0 -12
- package/backend/.sqlx/query-1b082630a9622f8667ee7a9aba2c2d3176019a68c6bb83d33008594821415a57.json +0 -12
- package/backend/.sqlx/query-1c7b06ba1e112abf6b945a2ff08a0b40ec23f3738c2e7399f067b558cf8d490e.json +0 -12
- package/backend/.sqlx/query-1f619f01f46859a64ded531dd0ef61abacfe62e758abe7030a6aa745140b95ca.json +0 -104
- package/backend/.sqlx/query-1fca1ce14b4b20205364cd1f1f45ebe1d2e30cd745e59e189d56487b5639dfbb.json +0 -12
- package/backend/.sqlx/query-212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef.json +0 -32
- package/backend/.sqlx/query-290ce5c152be8d36e58ff42570f9157beb07ab9e77a03ec6fc30b4f56f9b8f6b.json +0 -56
- package/backend/.sqlx/query-2b471d2c2e8ffbe0cd42d2a91b814c0d79f9d09200f147e3cea33ba4ce673c8a.json +0 -68
- package/backend/.sqlx/query-354a48c705bb9bb2048c1b7f10fcb714e23f9db82b7a4ea6932486197b2ede6a.json +0 -92
- package/backend/.sqlx/query-36c9e3dd10648e94b949db5c91a774ecb1e10a899ef95da74066eccedca4d8b2.json +0 -12
- package/backend/.sqlx/query-36e4ba7bbd81b402d5a20b6005755eafbb174c8dda442081823406ac32809a94.json +0 -56
- package/backend/.sqlx/query-3a5b3c98a55ca183ab20c74708e3d7e579dda37972c059e7515c4ceee4bd8dd3.json +0 -62
- package/backend/.sqlx/query-3d0a1cabf2a52e9d90cdfd29c509ca89aeb448d0c1d2446c65cd43db40735e86.json +0 -62
- package/backend/.sqlx/query-3d6bd16fbce59efe30b7f67ea342e0e4ea6d1432389c02468ad79f1f742d4031.json +0 -56
- package/backend/.sqlx/query-4049ca413b285a05aca6b25385e9c8185575f01e9069e4e8581aa45d713f612f.json +0 -32
- package/backend/.sqlx/query-412bacd3477d86369082e90f52240407abce436cb81292d42b2dbe1e5c18eea1.json +0 -104
- package/backend/.sqlx/query-417a8b1ff4e51de82aea0159a3b97932224dc325b23476cb84153d690227fd8b.json +0 -62
- package/backend/.sqlx/query-461cc1b0bb6fd909afc9dd2246e8526b3771cfbb0b22ae4b5d17b51af587b9e2.json +0 -56
- package/backend/.sqlx/query-58408c7a8cdeeda0bef359f1f9bd91299a339dc2b191462fc58c9736a56d5227.json +0 -92
- package/backend/.sqlx/query-5a886026d75d515c01f347cc203c8d99dd04c61dc468e2e4c5aa548436d13834.json +0 -62
- package/backend/.sqlx/query-5b902137b11022d2e1a5c4f6a9c83fec1a856c6a710aff831abd2382ede76b43.json +0 -12
- package/backend/.sqlx/query-5ed1238e52e59bb5f76c0f153fd99a14093f7ce2585bf9843585608f17ec575b.json +0 -104
- package/backend/.sqlx/query-6e8b860b14decfc2227dc57213f38442943d3fbef5c8418fd6b634c6e0f5e2ea.json +0 -104
- package/backend/.sqlx/query-6ec414276994c4ccb2433eaa5b1b342168557d17ddf5a52dac84cb1b59b9de8f.json +0 -68
- package/backend/.sqlx/query-6ecfa16d0cf825aacf233544b5baf151e9adfdca26c226ad71020d291fd802d5.json +0 -62
- package/backend/.sqlx/query-72509d252c39fce77520aa816cb2acbc1fb35dc2605e7be893610599b2427f2e.json +0 -62
- package/backend/.sqlx/query-75239b2da188f749707d77f3c1544332ca70db3d6d6743b2601dc0d167536437.json +0 -62
- package/backend/.sqlx/query-83d10e29f8478aff33434f9ac67068e013b888b953a2657e2bb72a6f619d04f2.json +0 -50
- package/backend/.sqlx/query-8610803360ea18b9b9d078a6981ea56abfbfe84e6354fc1d5ae4c622e01410ed.json +0 -68
- package/backend/.sqlx/query-86d03eb70eef39c59296416867f2ee66c9f7cd8b7f961fbda2f89fc0a1c442c2.json +0 -12
- package/backend/.sqlx/query-87d0feb5a6b442bad9c60068ea7569599cc6fc91a0e2692ecb42e93b03201b9d.json +0 -68
- package/backend/.sqlx/query-8a67b3b3337248f06a57bdf8a908f7ef23177431eaed82dc08c94c3e5944340e.json +0 -12
- package/backend/.sqlx/query-8f01ebd64bdcde6a090479f14810d73ba23020e76fd70854ac57f2da251702c3.json +0 -12
- package/backend/.sqlx/query-90fd607fcb2dca72239ff25e618e21e174b195991eaa33722cbf5f76da84cfab.json +0 -62
- package/backend/.sqlx/query-92e8bdbcd80c5ff3db7a35cf79492048803ef305cbdef0d0a1fe5dc881ca8c71.json +0 -104
- package/backend/.sqlx/query-93a1605f90e9672dad29b472b6ad85fa9a55ea3ffa5abcb8724b09d61be254ca.json +0 -20
- package/backend/.sqlx/query-9472c8fb477958167f5fae40b85ac44252468c5226b2cdd7770f027332eed6d7.json +0 -104
- package/backend/.sqlx/query-96036c4f9e0f48bdc5a4a4588f0c5f288ac7aaa5425cac40fc33f337e1a351f2.json +0 -56
- package/backend/.sqlx/query-9edb2c01e91fd0f0fe7b56e988c7ae0393150f50be3f419a981e035c0121dfc7.json +0 -104
- package/backend/.sqlx/query-a157cf00616f703bfba21927f1eb1c9eec2a81c02da15f66efdba0b6c375de1b.json +0 -26
- package/backend/.sqlx/query-a31fff84f3b8e532fd1160447d89d700f06ae08821fee00c9a5b60492b05259c.json +0 -62
- package/backend/.sqlx/query-a5ba908419fb3e456bdd2daca41ba06cc3212ffffb8520fc7dbbcc8b60ada314.json +0 -12
- package/backend/.sqlx/query-a6d2961718dbc3b1a925e549f49a159c561bef58c105529275f274b27e2eba5b.json +0 -104
- package/backend/.sqlx/query-a9e93d5b09b29faf66e387e4d7596a792d81e75c4d3726e83c2963e8d7c9b56f.json +0 -104
- package/backend/.sqlx/query-ac5247c8d7fb86e4650c4b0eb9420031614c831b7b085083bac20c1af314c538.json +0 -12
- package/backend/.sqlx/query-afef9467be74c411c4cb119a8b2b1aea53049877dfc30cc60b486134ba4b4c9f.json +0 -68
- package/backend/.sqlx/query-b2b2c6b4d0b1a347b5c4cb63c3a46a265d4db53be9554989a814b069d0af82f2.json +0 -62
- package/backend/.sqlx/query-c50d2ff0b12e5bcc81e371089ee2d007e233e7db93aefba4fef08e7aa68f5ab7.json +0 -20
- package/backend/.sqlx/query-c614e6056b244ca07f1b9d44e7edc9d5819225c6f8d9e077070c6e518a17f50b.json +0 -12
- package/backend/.sqlx/query-c67259be8bf4ee0cfd32167b2aa3b7fe9192809181a8171bf1c2d6df731967ae.json +0 -12
- package/backend/.sqlx/query-d2d0a1b985ebbca6a2b3e882a221a219f3199890fa640afc946ef1a792d6d8de.json +0 -12
- package/backend/.sqlx/query-d30aa5786757f32bf2b9c5fe51a45e506c71c28c5994e430d9b0546adb15ffa2.json +0 -20
- package/backend/.sqlx/query-d3b9ea1de1576af71b312924ce7f4ea8ae5dbe2ac138ea3b4470f2d5cd734846.json +0 -12
- package/backend/.sqlx/query-ed8456646fa69ddd412441955f06ff22bfb790f29466450735e0b8bb1bc4ec94.json +0 -12
- package/backend/Cargo.toml +0 -71
- package/backend/build.rs +0 -32
- package/backend/migrations/20250617183714_init.sql +0 -44
- package/backend/migrations/20250620212427_execution_processes.sql +0 -25
- package/backend/migrations/20250620214100_remove_stdout_stderr_from_task_attempts.sql +0 -28
- package/backend/migrations/20250621120000_relate_activities_to_execution_processes.sql +0 -23
- package/backend/migrations/20250623120000_executor_sessions.sql +0 -17
- package/backend/migrations/20250623130000_add_executor_type_to_execution_processes.sql +0 -4
- package/backend/migrations/20250625000000_add_dev_script_to_projects.sql +0 -4
- package/backend/migrations/20250701000000_add_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250701000001_add_pr_tracking_to_task_attempts.sql +0 -5
- package/backend/migrations/20250701120000_add_assistant_message_to_executor_sessions.sql +0 -2
- package/backend/migrations/20250708000000_add_base_branch_to_task_attempts.sql +0 -2
- package/backend/migrations/20250709000000_add_worktree_deleted_flag.sql +0 -2
- package/backend/migrations/20250710000000_add_setup_completion.sql +0 -3
- package/backend/migrations/20250715154859_add_task_templates.sql +0 -25
- package/backend/migrations/20250716143725_add_default_templates.sql +0 -174
- package/backend/migrations/20250716161432_update_executor_names_to_kebab_case.sql +0 -20
- package/backend/migrations/20250716170000_add_parent_task_to_tasks.sql +0 -7
- package/backend/migrations/20250717000000_drop_task_attempt_activities.sql +0 -9
- package/backend/migrations/20250719000000_add_cleanup_script_to_projects.sql +0 -2
- package/backend/migrations/20250720000000_add_cleanupscript_to_process_type_constraint.sql +0 -25
- package/backend/migrations/20250723000000_add_wish_to_tasks.sql +0 -7
- package/backend/migrations/20250724000000_remove_unique_wish_constraint.sql +0 -5
- package/backend/scripts/toast-notification.ps1 +0 -23
- package/backend/sounds/abstract-sound1.wav +0 -0
- package/backend/sounds/abstract-sound2.wav +0 -0
- package/backend/sounds/abstract-sound3.wav +0 -0
- package/backend/sounds/abstract-sound4.wav +0 -0
- package/backend/sounds/cow-mooing.wav +0 -0
- package/backend/sounds/phone-vibration.wav +0 -0
- package/backend/sounds/rooster.wav +0 -0
- package/backend/src/app_state.rs +0 -218
- package/backend/src/bin/generate_types.rs +0 -189
- package/backend/src/bin/mcp_task_server.rs +0 -191
- package/backend/src/execution_monitor.rs +0 -1193
- package/backend/src/executor.rs +0 -1053
- package/backend/src/executors/amp.rs +0 -697
- package/backend/src/executors/ccr.rs +0 -91
- package/backend/src/executors/charm_opencode.rs +0 -113
- package/backend/src/executors/claude.rs +0 -887
- package/backend/src/executors/cleanup_script.rs +0 -124
- package/backend/src/executors/dev_server.rs +0 -53
- package/backend/src/executors/echo.rs +0 -79
- package/backend/src/executors/gemini/config.rs +0 -67
- package/backend/src/executors/gemini/streaming.rs +0 -363
- package/backend/src/executors/gemini.rs +0 -765
- package/backend/src/executors/mod.rs +0 -23
- package/backend/src/executors/opencode_ai.rs +0 -113
- package/backend/src/executors/setup_script.rs +0 -130
- package/backend/src/executors/sst_opencode/filter.rs +0 -184
- package/backend/src/executors/sst_opencode/tools.rs +0 -139
- package/backend/src/executors/sst_opencode.rs +0 -756
- package/backend/src/lib.rs +0 -45
- package/backend/src/main.rs +0 -324
- package/backend/src/mcp/mod.rs +0 -1
- package/backend/src/mcp/task_server.rs +0 -850
- package/backend/src/middleware/mod.rs +0 -3
- package/backend/src/middleware/model_loaders.rs +0 -242
- package/backend/src/models/api_response.rs +0 -36
- package/backend/src/models/config.rs +0 -375
- package/backend/src/models/execution_process.rs +0 -430
- package/backend/src/models/executor_session.rs +0 -225
- package/backend/src/models/mod.rs +0 -12
- package/backend/src/models/project.rs +0 -356
- package/backend/src/models/task.rs +0 -345
- package/backend/src/models/task_attempt.rs +0 -1214
- package/backend/src/models/task_template.rs +0 -146
- package/backend/src/openapi.rs +0 -93
- package/backend/src/routes/auth.rs +0 -297
- package/backend/src/routes/config.rs +0 -385
- package/backend/src/routes/filesystem.rs +0 -228
- package/backend/src/routes/health.rs +0 -16
- package/backend/src/routes/mod.rs +0 -9
- package/backend/src/routes/projects.rs +0 -562
- package/backend/src/routes/stream.rs +0 -244
- package/backend/src/routes/task_attempts.rs +0 -1172
- package/backend/src/routes/task_templates.rs +0 -229
- package/backend/src/routes/tasks.rs +0 -353
- package/backend/src/services/analytics.rs +0 -216
- package/backend/src/services/git_service.rs +0 -1321
- package/backend/src/services/github_service.rs +0 -307
- package/backend/src/services/mod.rs +0 -13
- package/backend/src/services/notification_service.rs +0 -263
- package/backend/src/services/pr_monitor.rs +0 -214
- package/backend/src/services/process_service.rs +0 -940
- package/backend/src/utils/path.rs +0 -96
- package/backend/src/utils/shell.rs +0 -19
- package/backend/src/utils/text.rs +0 -24
- package/backend/src/utils/worktree_manager.rs +0 -578
- package/backend/src/utils.rs +0 -125
- package/backend/test.db +0 -0
- package/build-npm-package.sh +0 -61
- package/dev_assets_seed/config.json +0 -19
- package/frontend/.eslintrc.json +0 -25
- package/frontend/.prettierrc.json +0 -8
- package/frontend/components.json +0 -17
- package/frontend/index.html +0 -19
- package/frontend/package-lock.json +0 -7321
- package/frontend/package.json +0 -61
- package/frontend/postcss.config.js +0 -6
- package/frontend/public/android-chrome-192x192.png +0 -0
- package/frontend/public/android-chrome-512x512.png +0 -0
- package/frontend/public/apple-touch-icon.png +0 -0
- package/frontend/public/automagik-forge-logo-dark.svg +0 -3
- package/frontend/public/automagik-forge-logo.svg +0 -3
- package/frontend/public/automagik-forge-screenshot-overview.png +0 -0
- package/frontend/public/favicon-16x16.png +0 -0
- package/frontend/public/favicon-32x32.png +0 -0
- package/frontend/public/favicon.ico +0 -0
- package/frontend/public/site.webmanifest +0 -1
- package/frontend/public/viba-kanban-favicon.png +0 -0
- package/frontend/src/App.tsx +0 -157
- package/frontend/src/components/DisclaimerDialog.tsx +0 -106
- package/frontend/src/components/GitHubLoginDialog.tsx +0 -314
- package/frontend/src/components/OnboardingDialog.tsx +0 -185
- package/frontend/src/components/PrivacyOptInDialog.tsx +0 -130
- package/frontend/src/components/ProvidePatDialog.tsx +0 -98
- package/frontend/src/components/TaskTemplateManager.tsx +0 -336
- package/frontend/src/components/config-provider.tsx +0 -119
- package/frontend/src/components/context/TaskDetailsContextProvider.tsx +0 -470
- package/frontend/src/components/context/taskDetailsContext.ts +0 -125
- package/frontend/src/components/keyboard-shortcuts-demo.tsx +0 -35
- package/frontend/src/components/layout/navbar.tsx +0 -86
- package/frontend/src/components/logo.tsx +0 -44
- package/frontend/src/components/projects/ProjectCard.tsx +0 -155
- package/frontend/src/components/projects/project-detail.tsx +0 -251
- package/frontend/src/components/projects/project-form-fields.tsx +0 -238
- package/frontend/src/components/projects/project-form.tsx +0 -301
- package/frontend/src/components/projects/project-list.tsx +0 -200
- package/frontend/src/components/projects/projects-page.tsx +0 -20
- package/frontend/src/components/tasks/BranchSelector.tsx +0 -169
- package/frontend/src/components/tasks/DeleteFileConfirmationDialog.tsx +0 -94
- package/frontend/src/components/tasks/EditorSelectionDialog.tsx +0 -119
- package/frontend/src/components/tasks/TaskCard.tsx +0 -154
- package/frontend/src/components/tasks/TaskDetails/CollapsibleToolbar.tsx +0 -33
- package/frontend/src/components/tasks/TaskDetails/DiffCard.tsx +0 -109
- package/frontend/src/components/tasks/TaskDetails/DiffChunkSection.tsx +0 -135
- package/frontend/src/components/tasks/TaskDetails/DiffFile.tsx +0 -296
- package/frontend/src/components/tasks/TaskDetails/DiffTab.tsx +0 -32
- package/frontend/src/components/tasks/TaskDetails/DisplayConversationEntry.tsx +0 -392
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Conversation.tsx +0 -256
- package/frontend/src/components/tasks/TaskDetails/LogsTab/ConversationEntry.tsx +0 -56
- package/frontend/src/components/tasks/TaskDetails/LogsTab/NormalizedConversationViewer.tsx +0 -92
- package/frontend/src/components/tasks/TaskDetails/LogsTab/Prompt.tsx +0 -22
- package/frontend/src/components/tasks/TaskDetails/LogsTab/SetupScriptRunning.tsx +0 -49
- package/frontend/src/components/tasks/TaskDetails/LogsTab.tsx +0 -186
- package/frontend/src/components/tasks/TaskDetails/ProcessesTab.tsx +0 -288
- package/frontend/src/components/tasks/TaskDetails/RelatedTasksTab.tsx +0 -216
- package/frontend/src/components/tasks/TaskDetails/TabNavigation.tsx +0 -93
- package/frontend/src/components/tasks/TaskDetailsHeader.tsx +0 -169
- package/frontend/src/components/tasks/TaskDetailsPanel.tsx +0 -126
- package/frontend/src/components/tasks/TaskDetailsToolbar.tsx +0 -302
- package/frontend/src/components/tasks/TaskFollowUpSection.tsx +0 -130
- package/frontend/src/components/tasks/TaskFormDialog.tsx +0 -400
- package/frontend/src/components/tasks/TaskKanbanBoard.tsx +0 -180
- package/frontend/src/components/tasks/Toolbar/CreateAttempt.tsx +0 -259
- package/frontend/src/components/tasks/Toolbar/CreatePRDialog.tsx +0 -243
- package/frontend/src/components/tasks/Toolbar/CurrentAttempt.tsx +0 -899
- package/frontend/src/components/tasks/index.ts +0 -2
- package/frontend/src/components/theme-provider.tsx +0 -82
- package/frontend/src/components/theme-toggle.tsx +0 -36
- package/frontend/src/components/ui/alert.tsx +0 -59
- package/frontend/src/components/ui/auto-expanding-textarea.tsx +0 -70
- package/frontend/src/components/ui/badge.tsx +0 -36
- package/frontend/src/components/ui/button.tsx +0 -56
- package/frontend/src/components/ui/card.tsx +0 -86
- package/frontend/src/components/ui/checkbox.tsx +0 -44
- package/frontend/src/components/ui/chip.tsx +0 -25
- package/frontend/src/components/ui/dialog.tsx +0 -124
- package/frontend/src/components/ui/dropdown-menu.tsx +0 -198
- package/frontend/src/components/ui/file-search-textarea.tsx +0 -292
- package/frontend/src/components/ui/folder-picker.tsx +0 -279
- package/frontend/src/components/ui/input.tsx +0 -25
- package/frontend/src/components/ui/label.tsx +0 -24
- package/frontend/src/components/ui/loader.tsx +0 -26
- package/frontend/src/components/ui/markdown-renderer.tsx +0 -75
- package/frontend/src/components/ui/select.tsx +0 -160
- package/frontend/src/components/ui/separator.tsx +0 -31
- package/frontend/src/components/ui/shadcn-io/kanban/index.tsx +0 -185
- package/frontend/src/components/ui/table.tsx +0 -117
- package/frontend/src/components/ui/tabs.tsx +0 -53
- package/frontend/src/components/ui/textarea.tsx +0 -22
- package/frontend/src/components/ui/tooltip.tsx +0 -28
- package/frontend/src/hooks/useNormalizedConversation.ts +0 -440
- package/frontend/src/index.css +0 -225
- package/frontend/src/lib/api.ts +0 -630
- package/frontend/src/lib/keyboard-shortcuts.ts +0 -266
- package/frontend/src/lib/responsive-config.ts +0 -70
- package/frontend/src/lib/types.ts +0 -39
- package/frontend/src/lib/utils.ts +0 -10
- package/frontend/src/main.tsx +0 -50
- package/frontend/src/pages/McpServers.tsx +0 -418
- package/frontend/src/pages/Settings.tsx +0 -610
- package/frontend/src/pages/project-tasks.tsx +0 -575
- package/frontend/src/pages/projects.tsx +0 -18
- package/frontend/src/vite-env.d.ts +0 -1
- package/frontend/tailwind.config.js +0 -125
- package/frontend/tsconfig.json +0 -26
- package/frontend/tsconfig.node.json +0 -10
- package/frontend/vite.config.ts +0 -33
- package/npx-cli/README.md +0 -159
- package/npx-cli/automagik-forge-0.1.0.tgz +0 -0
- package/npx-cli/automagik-forge-0.1.10.tgz +0 -0
- package/npx-cli/package.json +0 -17
- package/npx-cli/vibe-kanban-0.0.55.tgz +0 -0
- package/pnpm-workspace.yaml +0 -2
- package/rust-toolchain.toml +0 -11
- package/rustfmt.toml +0 -3
- package/scripts/load-env.js +0 -43
- package/scripts/mcp_test.js +0 -374
- package/scripts/prepare-db.js +0 -45
- package/scripts/setup-dev-environment.js +0 -274
- package/scripts/start-mcp-sse.js +0 -70
- package/scripts/test-debug.js +0 -32
- package/scripts/test-mcp-sse.js +0 -138
- package/scripts/test-simple.js +0 -44
- package/scripts/test-wish-final.js +0 -179
- package/scripts/test-wish-system.js +0 -221
- package/shared/types.ts +0 -182
- package/test-npm-package.sh +0 -42
- /package/{npx-cli/bin → bin}/cli.js +0 -0
@@ -1,385 +0,0 @@
|
|
1
|
-
use std::collections::HashMap;
|
2
|
-
|
3
|
-
use axum::{
|
4
|
-
extract::{Query, State},
|
5
|
-
response::Json as ResponseJson,
|
6
|
-
routing::{get, post},
|
7
|
-
Json, Router,
|
8
|
-
};
|
9
|
-
use serde::{Deserialize, Serialize};
|
10
|
-
use serde_json::Value;
|
11
|
-
use tokio::fs;
|
12
|
-
use ts_rs::TS;
|
13
|
-
use utoipa::ToSchema;
|
14
|
-
|
15
|
-
use crate::{
|
16
|
-
app_state::AppState,
|
17
|
-
executor::ExecutorConfig,
|
18
|
-
models::{
|
19
|
-
config::{Config, EditorConstants, SoundConstants},
|
20
|
-
ApiResponse,
|
21
|
-
},
|
22
|
-
utils,
|
23
|
-
};
|
24
|
-
|
25
|
-
pub fn config_router() -> Router<AppState> {
|
26
|
-
Router::new()
|
27
|
-
.route("/config", get(get_config))
|
28
|
-
.route("/config", post(update_config))
|
29
|
-
.route("/config/constants", get(get_config_constants))
|
30
|
-
.route("/mcp-servers", get(get_mcp_servers))
|
31
|
-
.route("/mcp-servers", post(update_mcp_servers))
|
32
|
-
}
|
33
|
-
|
34
|
-
#[utoipa::path(
|
35
|
-
get,
|
36
|
-
path = "/config",
|
37
|
-
tag = "config",
|
38
|
-
summary = "Get application configuration",
|
39
|
-
description = "Retrieves the current application configuration settings",
|
40
|
-
responses(
|
41
|
-
(status = 200, description = "Configuration retrieved successfully", body = ApiResponse<Config>)
|
42
|
-
)
|
43
|
-
)]
|
44
|
-
pub async fn get_config(State(app_state): State<AppState>) -> ResponseJson<ApiResponse<Config>> {
|
45
|
-
let config = app_state.get_config().read().await;
|
46
|
-
ResponseJson(ApiResponse::success(config.clone()))
|
47
|
-
}
|
48
|
-
|
49
|
-
#[utoipa::path(
|
50
|
-
post,
|
51
|
-
path = "/config",
|
52
|
-
tag = "config",
|
53
|
-
summary = "Update application configuration",
|
54
|
-
description = "Updates the application configuration with new settings",
|
55
|
-
request_body = Config,
|
56
|
-
responses(
|
57
|
-
(status = 200, description = "Configuration updated successfully", body = ApiResponse<Config>),
|
58
|
-
(status = 500, description = "Failed to save configuration", body = ApiResponse<String>)
|
59
|
-
)
|
60
|
-
)]
|
61
|
-
pub async fn update_config(
|
62
|
-
State(app_state): State<AppState>,
|
63
|
-
Json(new_config): Json<Config>,
|
64
|
-
) -> ResponseJson<ApiResponse<Config>> {
|
65
|
-
let config_path = utils::config_path();
|
66
|
-
|
67
|
-
match new_config.save(&config_path) {
|
68
|
-
Ok(_) => {
|
69
|
-
let mut config = app_state.get_config().write().await;
|
70
|
-
*config = new_config.clone();
|
71
|
-
drop(config);
|
72
|
-
|
73
|
-
app_state
|
74
|
-
.update_analytics_config(new_config.analytics_enabled.unwrap_or(true))
|
75
|
-
.await;
|
76
|
-
|
77
|
-
ResponseJson(ApiResponse::success(new_config))
|
78
|
-
}
|
79
|
-
Err(e) => ResponseJson(ApiResponse::error(&format!("Failed to save config: {}", e))),
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
#[derive(Debug, Serialize, Deserialize, TS, ToSchema)]
|
84
|
-
#[ts(export)]
|
85
|
-
pub struct ConfigConstants {
|
86
|
-
pub editor: EditorConstants,
|
87
|
-
pub sound: SoundConstants,
|
88
|
-
}
|
89
|
-
|
90
|
-
#[utoipa::path(
|
91
|
-
get,
|
92
|
-
path = "/config/constants",
|
93
|
-
tag = "config",
|
94
|
-
summary = "Get configuration constants",
|
95
|
-
description = "Retrieves editor and sound constants for the application",
|
96
|
-
responses(
|
97
|
-
(status = 200, description = "Constants retrieved successfully", body = ApiResponse<ConfigConstants>)
|
98
|
-
)
|
99
|
-
)]
|
100
|
-
pub async fn get_config_constants() -> ResponseJson<ApiResponse<ConfigConstants>> {
|
101
|
-
let constants = ConfigConstants {
|
102
|
-
editor: EditorConstants::new(),
|
103
|
-
sound: SoundConstants::new(),
|
104
|
-
};
|
105
|
-
|
106
|
-
ResponseJson(ApiResponse::success(constants))
|
107
|
-
}
|
108
|
-
|
109
|
-
#[derive(Debug, Deserialize, ToSchema)]
|
110
|
-
pub struct McpServerQuery {
|
111
|
-
executor: Option<String>,
|
112
|
-
}
|
113
|
-
|
114
|
-
/// Common logic for resolving executor configuration and validating MCP support
|
115
|
-
fn resolve_executor_config(
|
116
|
-
query_executor: Option<String>,
|
117
|
-
saved_config: &ExecutorConfig,
|
118
|
-
) -> Result<ExecutorConfig, String> {
|
119
|
-
let executor_config = match query_executor {
|
120
|
-
Some(executor_type) => executor_type
|
121
|
-
.parse::<ExecutorConfig>()
|
122
|
-
.map_err(|e| e.to_string())?,
|
123
|
-
None => saved_config.clone(),
|
124
|
-
};
|
125
|
-
|
126
|
-
if !executor_config.supports_mcp() {
|
127
|
-
return Err(format!(
|
128
|
-
"{} executor does not support MCP configuration",
|
129
|
-
executor_config.display_name()
|
130
|
-
));
|
131
|
-
}
|
132
|
-
|
133
|
-
Ok(executor_config)
|
134
|
-
}
|
135
|
-
|
136
|
-
#[utoipa::path(
|
137
|
-
get,
|
138
|
-
path = "/mcp-servers",
|
139
|
-
tag = "config",
|
140
|
-
summary = "Get MCP servers configuration",
|
141
|
-
description = "Retrieves MCP (Model Context Protocol) servers configuration for the specified executor",
|
142
|
-
params(
|
143
|
-
("executor" = Option<String>, Query, description = "Executor type to get MCP servers for")
|
144
|
-
),
|
145
|
-
responses(
|
146
|
-
(status = 200, description = "MCP servers retrieved successfully", body = ApiResponse<Value>),
|
147
|
-
(status = 400, description = "Executor does not support MCP or invalid configuration", body = ApiResponse<String>)
|
148
|
-
)
|
149
|
-
)]
|
150
|
-
pub async fn get_mcp_servers(
|
151
|
-
State(app_state): State<AppState>,
|
152
|
-
Query(query): Query<McpServerQuery>,
|
153
|
-
) -> ResponseJson<ApiResponse<Value>> {
|
154
|
-
let saved_config = {
|
155
|
-
let config = app_state.get_config().read().await;
|
156
|
-
config.executor.clone()
|
157
|
-
};
|
158
|
-
|
159
|
-
let executor_config = match resolve_executor_config(query.executor, &saved_config) {
|
160
|
-
Ok(config) => config,
|
161
|
-
Err(message) => {
|
162
|
-
return ResponseJson(ApiResponse::error(&message));
|
163
|
-
}
|
164
|
-
};
|
165
|
-
|
166
|
-
// Get the config file path for this executor
|
167
|
-
let config_path = match executor_config.config_path() {
|
168
|
-
Some(path) => path,
|
169
|
-
None => {
|
170
|
-
return ResponseJson(ApiResponse::error("Could not determine config file path"));
|
171
|
-
}
|
172
|
-
};
|
173
|
-
|
174
|
-
match read_mcp_servers_from_config(&config_path, &executor_config).await {
|
175
|
-
Ok(servers) => {
|
176
|
-
let response_data = serde_json::json!({
|
177
|
-
"servers": servers,
|
178
|
-
"config_path": config_path.to_string_lossy().to_string()
|
179
|
-
});
|
180
|
-
ResponseJson(ApiResponse::success(response_data))
|
181
|
-
}
|
182
|
-
Err(e) => ResponseJson(ApiResponse::error(&format!(
|
183
|
-
"Failed to read MCP servers: {}",
|
184
|
-
e
|
185
|
-
))),
|
186
|
-
}
|
187
|
-
}
|
188
|
-
|
189
|
-
#[utoipa::path(
|
190
|
-
post,
|
191
|
-
path = "/mcp-servers",
|
192
|
-
tag = "config",
|
193
|
-
summary = "Update MCP servers configuration",
|
194
|
-
description = "Updates MCP (Model Context Protocol) servers configuration for the specified executor",
|
195
|
-
params(
|
196
|
-
("executor" = Option<String>, Query, description = "Executor type to update MCP servers for")
|
197
|
-
),
|
198
|
-
request_body = HashMap<String, Value>,
|
199
|
-
responses(
|
200
|
-
(status = 200, description = "MCP servers updated successfully", body = ApiResponse<String>),
|
201
|
-
(status = 400, description = "Executor does not support MCP or update failed", body = ApiResponse<String>)
|
202
|
-
)
|
203
|
-
)]
|
204
|
-
pub async fn update_mcp_servers(
|
205
|
-
State(app_state): State<AppState>,
|
206
|
-
Query(query): Query<McpServerQuery>,
|
207
|
-
Json(new_servers): Json<HashMap<String, Value>>,
|
208
|
-
) -> ResponseJson<ApiResponse<String>> {
|
209
|
-
let saved_config = {
|
210
|
-
let config = app_state.get_config().read().await;
|
211
|
-
config.executor.clone()
|
212
|
-
};
|
213
|
-
|
214
|
-
let executor_config = match resolve_executor_config(query.executor, &saved_config) {
|
215
|
-
Ok(config) => config,
|
216
|
-
Err(message) => {
|
217
|
-
return ResponseJson(ApiResponse::error(&message));
|
218
|
-
}
|
219
|
-
};
|
220
|
-
|
221
|
-
// Get the config file path for this executor
|
222
|
-
let config_path = match executor_config.config_path() {
|
223
|
-
Some(path) => path,
|
224
|
-
None => {
|
225
|
-
return ResponseJson(ApiResponse::error("Could not determine config file path"));
|
226
|
-
}
|
227
|
-
};
|
228
|
-
|
229
|
-
match update_mcp_servers_in_config(&config_path, &executor_config, new_servers).await {
|
230
|
-
Ok(message) => ResponseJson(ApiResponse::success(message)),
|
231
|
-
Err(e) => ResponseJson(ApiResponse::error(&format!(
|
232
|
-
"Failed to update MCP servers: {}",
|
233
|
-
e
|
234
|
-
))),
|
235
|
-
}
|
236
|
-
}
|
237
|
-
|
238
|
-
async fn update_mcp_servers_in_config(
|
239
|
-
file_path: &std::path::Path,
|
240
|
-
executor_config: &ExecutorConfig,
|
241
|
-
new_servers: HashMap<String, Value>,
|
242
|
-
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
243
|
-
// Ensure parent directory exists
|
244
|
-
if let Some(parent) = file_path.parent() {
|
245
|
-
fs::create_dir_all(parent).await?;
|
246
|
-
}
|
247
|
-
|
248
|
-
// Read existing config file or create empty object if it doesn't exist
|
249
|
-
let file_content = fs::read_to_string(file_path)
|
250
|
-
.await
|
251
|
-
.unwrap_or_else(|_| "{}".to_string());
|
252
|
-
let mut config: Value = serde_json::from_str(&file_content)?;
|
253
|
-
|
254
|
-
// Get the attribute path for MCP servers
|
255
|
-
let mcp_path = executor_config.mcp_attribute_path().unwrap();
|
256
|
-
|
257
|
-
// Get the current server count for comparison
|
258
|
-
let old_servers = get_mcp_servers_from_config_path(&config, &mcp_path).len();
|
259
|
-
|
260
|
-
// Set the MCP servers using the correct attribute path
|
261
|
-
set_mcp_servers_in_config_path(&mut config, &mcp_path, &new_servers)?;
|
262
|
-
|
263
|
-
// Write the updated config back to file
|
264
|
-
let updated_content = serde_json::to_string_pretty(&config)?;
|
265
|
-
fs::write(file_path, updated_content).await?;
|
266
|
-
|
267
|
-
let new_count = new_servers.len();
|
268
|
-
let message = match (old_servers, new_count) {
|
269
|
-
(0, 0) => "No MCP servers configured".to_string(),
|
270
|
-
(0, n) => format!("Added {} MCP server(s)", n),
|
271
|
-
(old, new) if old == new => format!("Updated MCP server configuration ({} server(s))", new),
|
272
|
-
(old, new) => format!(
|
273
|
-
"Updated MCP server configuration (was {}, now {})",
|
274
|
-
old, new
|
275
|
-
),
|
276
|
-
};
|
277
|
-
|
278
|
-
Ok(message)
|
279
|
-
}
|
280
|
-
|
281
|
-
async fn read_mcp_servers_from_config(
|
282
|
-
file_path: &std::path::Path,
|
283
|
-
executor_config: &ExecutorConfig,
|
284
|
-
) -> Result<HashMap<String, Value>, Box<dyn std::error::Error + Send + Sync>> {
|
285
|
-
// Read the config file, return empty if it doesn't exist
|
286
|
-
let file_content = fs::read_to_string(file_path)
|
287
|
-
.await
|
288
|
-
.unwrap_or_else(|_| "{}".to_string());
|
289
|
-
let config: Value = serde_json::from_str(&file_content)?;
|
290
|
-
|
291
|
-
// Get the attribute path for MCP servers
|
292
|
-
let mcp_path = executor_config.mcp_attribute_path().unwrap();
|
293
|
-
|
294
|
-
// Get the servers using the correct attribute path
|
295
|
-
let servers = get_mcp_servers_from_config_path(&config, &mcp_path);
|
296
|
-
|
297
|
-
Ok(servers)
|
298
|
-
}
|
299
|
-
|
300
|
-
/// Helper function to get MCP servers from config using a path
|
301
|
-
fn get_mcp_servers_from_config_path(config: &Value, path: &[&str]) -> HashMap<String, Value> {
|
302
|
-
// Special handling for AMP - use flat key structure
|
303
|
-
if path.len() == 2 && path[0] == "amp" && path[1] == "mcpServers" {
|
304
|
-
let flat_key = format!("{}.{}", path[0], path[1]);
|
305
|
-
let current = match config.get(&flat_key) {
|
306
|
-
Some(val) => val,
|
307
|
-
None => return HashMap::new(),
|
308
|
-
};
|
309
|
-
|
310
|
-
// Extract the servers object
|
311
|
-
match current.as_object() {
|
312
|
-
Some(servers) => servers
|
313
|
-
.iter()
|
314
|
-
.map(|(k, v)| (k.clone(), v.clone()))
|
315
|
-
.collect(),
|
316
|
-
None => HashMap::new(),
|
317
|
-
}
|
318
|
-
} else {
|
319
|
-
let mut current = config;
|
320
|
-
|
321
|
-
// Navigate to the target location
|
322
|
-
for &part in path {
|
323
|
-
current = match current.get(part) {
|
324
|
-
Some(val) => val,
|
325
|
-
None => return HashMap::new(),
|
326
|
-
};
|
327
|
-
}
|
328
|
-
|
329
|
-
// Extract the servers object
|
330
|
-
match current.as_object() {
|
331
|
-
Some(servers) => servers
|
332
|
-
.iter()
|
333
|
-
.map(|(k, v)| (k.clone(), v.clone()))
|
334
|
-
.collect(),
|
335
|
-
None => HashMap::new(),
|
336
|
-
}
|
337
|
-
}
|
338
|
-
}
|
339
|
-
|
340
|
-
/// Helper function to set MCP servers in config using a path
|
341
|
-
fn set_mcp_servers_in_config_path(
|
342
|
-
config: &mut Value,
|
343
|
-
path: &[&str],
|
344
|
-
servers: &HashMap<String, Value>,
|
345
|
-
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
346
|
-
// Ensure config is an object
|
347
|
-
if !config.is_object() {
|
348
|
-
*config = serde_json::json!({});
|
349
|
-
}
|
350
|
-
|
351
|
-
// Special handling for AMP - use flat key structure
|
352
|
-
if path.len() == 2 && path[0] == "amp" && path[1] == "mcpServers" {
|
353
|
-
let flat_key = format!("{}.{}", path[0], path[1]);
|
354
|
-
config
|
355
|
-
.as_object_mut()
|
356
|
-
.unwrap()
|
357
|
-
.insert(flat_key, serde_json::to_value(servers)?);
|
358
|
-
return Ok(());
|
359
|
-
}
|
360
|
-
|
361
|
-
let mut current = config;
|
362
|
-
|
363
|
-
// Navigate/create the nested structure (all parts except the last)
|
364
|
-
for &part in &path[..path.len() - 1] {
|
365
|
-
if current.get(part).is_none() {
|
366
|
-
current
|
367
|
-
.as_object_mut()
|
368
|
-
.unwrap()
|
369
|
-
.insert(part.to_string(), serde_json::json!({}));
|
370
|
-
}
|
371
|
-
current = current.get_mut(part).unwrap();
|
372
|
-
if !current.is_object() {
|
373
|
-
*current = serde_json::json!({});
|
374
|
-
}
|
375
|
-
}
|
376
|
-
|
377
|
-
// Set the final attribute
|
378
|
-
let final_attr = path.last().unwrap();
|
379
|
-
current
|
380
|
-
.as_object_mut()
|
381
|
-
.unwrap()
|
382
|
-
.insert(final_attr.to_string(), serde_json::to_value(servers)?);
|
383
|
-
|
384
|
-
Ok(())
|
385
|
-
}
|
@@ -1,228 +0,0 @@
|
|
1
|
-
use std::{
|
2
|
-
fs,
|
3
|
-
path::{Path, PathBuf},
|
4
|
-
};
|
5
|
-
|
6
|
-
use axum::{
|
7
|
-
extract::Query, http::StatusCode, response::Json as ResponseJson, routing::get, Router,
|
8
|
-
};
|
9
|
-
use serde::{Deserialize, Serialize};
|
10
|
-
use ts_rs::TS;
|
11
|
-
use utoipa::ToSchema;
|
12
|
-
|
13
|
-
use crate::{app_state::AppState, models::ApiResponse};
|
14
|
-
|
15
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
16
|
-
#[ts(export)]
|
17
|
-
pub struct DirectoryEntry {
|
18
|
-
pub name: String,
|
19
|
-
pub path: String,
|
20
|
-
pub is_directory: bool,
|
21
|
-
pub is_git_repo: bool,
|
22
|
-
}
|
23
|
-
|
24
|
-
#[derive(Debug, Serialize, TS, ToSchema)]
|
25
|
-
#[ts(export)]
|
26
|
-
pub struct DirectoryListResponse {
|
27
|
-
pub entries: Vec<DirectoryEntry>,
|
28
|
-
pub current_path: String,
|
29
|
-
}
|
30
|
-
|
31
|
-
#[derive(Debug, Deserialize, ToSchema)]
|
32
|
-
pub struct ListDirectoryQuery {
|
33
|
-
path: Option<String>,
|
34
|
-
}
|
35
|
-
|
36
|
-
#[utoipa::path(
|
37
|
-
get,
|
38
|
-
path = "/filesystem/list",
|
39
|
-
tag = "filesystem",
|
40
|
-
summary = "List directory contents",
|
41
|
-
description = "Lists files and directories at the specified path, with Git repository detection",
|
42
|
-
params(
|
43
|
-
("path" = Option<String>, Query, description = "Directory path to list (defaults to home directory)")
|
44
|
-
),
|
45
|
-
responses(
|
46
|
-
(status = 200, description = "Directory contents retrieved successfully", body = ApiResponse<DirectoryListResponse>),
|
47
|
-
(status = 404, description = "Directory not found or access denied")
|
48
|
-
)
|
49
|
-
)]
|
50
|
-
pub async fn list_directory(
|
51
|
-
Query(query): Query<ListDirectoryQuery>,
|
52
|
-
) -> Result<ResponseJson<ApiResponse<DirectoryListResponse>>, StatusCode> {
|
53
|
-
let path_str = query.path.unwrap_or_else(|| {
|
54
|
-
// Default to user's home directory
|
55
|
-
dirs::home_dir()
|
56
|
-
.or_else(dirs::desktop_dir)
|
57
|
-
.or_else(dirs::document_dir)
|
58
|
-
.unwrap_or_else(|| {
|
59
|
-
if cfg!(windows) {
|
60
|
-
std::env::var("USERPROFILE")
|
61
|
-
.map(PathBuf::from)
|
62
|
-
.unwrap_or_else(|_| PathBuf::from("C:\\"))
|
63
|
-
} else {
|
64
|
-
PathBuf::from("/")
|
65
|
-
}
|
66
|
-
})
|
67
|
-
.to_string_lossy()
|
68
|
-
.to_string()
|
69
|
-
});
|
70
|
-
|
71
|
-
let path = Path::new(&path_str);
|
72
|
-
|
73
|
-
if !path.exists() {
|
74
|
-
return Ok(ResponseJson(ApiResponse::error("Directory does not exist")));
|
75
|
-
}
|
76
|
-
|
77
|
-
if !path.is_dir() {
|
78
|
-
return Ok(ResponseJson(ApiResponse::error("Path is not a directory")));
|
79
|
-
}
|
80
|
-
|
81
|
-
match fs::read_dir(path) {
|
82
|
-
Ok(entries) => {
|
83
|
-
let mut directory_entries = Vec::new();
|
84
|
-
|
85
|
-
for entry in entries.flatten() {
|
86
|
-
let path = entry.path();
|
87
|
-
let metadata = entry.metadata().ok();
|
88
|
-
|
89
|
-
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
90
|
-
// Skip hidden files/directories
|
91
|
-
if name.starts_with('.') && name != ".." {
|
92
|
-
continue;
|
93
|
-
}
|
94
|
-
|
95
|
-
let is_directory = metadata.is_some_and(|m| m.is_dir());
|
96
|
-
let is_git_repo = if is_directory {
|
97
|
-
path.join(".git").exists()
|
98
|
-
} else {
|
99
|
-
false
|
100
|
-
};
|
101
|
-
|
102
|
-
directory_entries.push(DirectoryEntry {
|
103
|
-
name: name.to_string(),
|
104
|
-
path: path.to_string_lossy().to_string(),
|
105
|
-
is_directory,
|
106
|
-
is_git_repo,
|
107
|
-
});
|
108
|
-
}
|
109
|
-
}
|
110
|
-
|
111
|
-
// Sort: directories first, then files, both alphabetically
|
112
|
-
directory_entries.sort_by(|a, b| match (a.is_directory, b.is_directory) {
|
113
|
-
(true, false) => std::cmp::Ordering::Less,
|
114
|
-
(false, true) => std::cmp::Ordering::Greater,
|
115
|
-
_ => a.name.to_lowercase().cmp(&b.name.to_lowercase()),
|
116
|
-
});
|
117
|
-
|
118
|
-
Ok(ResponseJson(ApiResponse::success(DirectoryListResponse {
|
119
|
-
entries: directory_entries,
|
120
|
-
current_path: path.to_string_lossy().to_string(),
|
121
|
-
})))
|
122
|
-
}
|
123
|
-
Err(e) => {
|
124
|
-
tracing::error!("Failed to read directory: {}", e);
|
125
|
-
Ok(ResponseJson(ApiResponse::error(&format!(
|
126
|
-
"Failed to read directory: {}",
|
127
|
-
e
|
128
|
-
))))
|
129
|
-
}
|
130
|
-
}
|
131
|
-
}
|
132
|
-
|
133
|
-
#[utoipa::path(
|
134
|
-
get,
|
135
|
-
path = "/filesystem/validate-git",
|
136
|
-
tag = "filesystem",
|
137
|
-
summary = "Validate Git repository path",
|
138
|
-
description = "Checks if the specified path exists and is a valid Git repository",
|
139
|
-
params(
|
140
|
-
("path" = String, Query, description = "Directory path to validate as Git repository")
|
141
|
-
),
|
142
|
-
responses(
|
143
|
-
(status = 200, description = "Path validation result", body = ApiResponse<bool>),
|
144
|
-
(status = 400, description = "Missing or invalid path parameter")
|
145
|
-
)
|
146
|
-
)]
|
147
|
-
pub async fn validate_git_path(
|
148
|
-
Query(query): Query<ListDirectoryQuery>,
|
149
|
-
) -> Result<ResponseJson<ApiResponse<bool>>, StatusCode> {
|
150
|
-
let path_str = query.path.ok_or(StatusCode::BAD_REQUEST)?;
|
151
|
-
let path = Path::new(&path_str);
|
152
|
-
|
153
|
-
// Check if path exists and is a git repo
|
154
|
-
let is_valid_git_repo = path.exists() && path.is_dir() && path.join(".git").exists();
|
155
|
-
|
156
|
-
Ok(ResponseJson(ApiResponse::success(is_valid_git_repo)))
|
157
|
-
}
|
158
|
-
|
159
|
-
#[utoipa::path(
|
160
|
-
get,
|
161
|
-
path = "/filesystem/create-git",
|
162
|
-
tag = "filesystem",
|
163
|
-
summary = "Initialize Git repository",
|
164
|
-
description = "Creates a directory if needed and initializes it as a Git repository",
|
165
|
-
params(
|
166
|
-
("path" = String, Query, description = "Directory path where Git repository should be created")
|
167
|
-
),
|
168
|
-
responses(
|
169
|
-
(status = 200, description = "Git repository created or already exists"),
|
170
|
-
(status = 400, description = "Missing path parameter or Git initialization failed")
|
171
|
-
)
|
172
|
-
)]
|
173
|
-
pub async fn create_git_repo(
|
174
|
-
Query(query): Query<ListDirectoryQuery>,
|
175
|
-
) -> Result<ResponseJson<ApiResponse<()>>, StatusCode> {
|
176
|
-
let path_str = query.path.ok_or(StatusCode::BAD_REQUEST)?;
|
177
|
-
let path = Path::new(&path_str);
|
178
|
-
|
179
|
-
// Create directory if it doesn't exist
|
180
|
-
if !path.exists() {
|
181
|
-
if let Err(e) = fs::create_dir_all(path) {
|
182
|
-
tracing::error!("Failed to create directory: {}", e);
|
183
|
-
return Ok(ResponseJson(ApiResponse::error(&format!(
|
184
|
-
"Failed to create directory: {}",
|
185
|
-
e
|
186
|
-
))));
|
187
|
-
}
|
188
|
-
}
|
189
|
-
|
190
|
-
// Check if it's already a git repo
|
191
|
-
if path.join(".git").exists() {
|
192
|
-
return Ok(ResponseJson(ApiResponse::success(())));
|
193
|
-
}
|
194
|
-
|
195
|
-
// Initialize git repository
|
196
|
-
match std::process::Command::new("git")
|
197
|
-
.arg("init")
|
198
|
-
.current_dir(path)
|
199
|
-
.output()
|
200
|
-
{
|
201
|
-
Ok(output) => {
|
202
|
-
if output.status.success() {
|
203
|
-
Ok(ResponseJson(ApiResponse::success(())))
|
204
|
-
} else {
|
205
|
-
let error_msg = String::from_utf8_lossy(&output.stderr);
|
206
|
-
tracing::error!("Git init failed: {}", error_msg);
|
207
|
-
Ok(ResponseJson(ApiResponse::error(&format!(
|
208
|
-
"Git init failed: {}",
|
209
|
-
error_msg
|
210
|
-
))))
|
211
|
-
}
|
212
|
-
}
|
213
|
-
Err(e) => {
|
214
|
-
tracing::error!("Failed to run git init: {}", e);
|
215
|
-
Ok(ResponseJson(ApiResponse::error(&format!(
|
216
|
-
"Failed to run git init: {}",
|
217
|
-
e
|
218
|
-
))))
|
219
|
-
}
|
220
|
-
}
|
221
|
-
}
|
222
|
-
|
223
|
-
pub fn filesystem_router() -> Router<AppState> {
|
224
|
-
Router::new()
|
225
|
-
.route("/filesystem/list", get(list_directory))
|
226
|
-
.route("/filesystem/validate-git", get(validate_git_path))
|
227
|
-
.route("/filesystem/create-git", get(create_git_repo))
|
228
|
-
}
|
@@ -1,16 +0,0 @@
|
|
1
|
-
use axum::response::Json;
|
2
|
-
use utoipa;
|
3
|
-
|
4
|
-
use crate::models::ApiResponse;
|
5
|
-
|
6
|
-
#[utoipa::path(
|
7
|
-
get,
|
8
|
-
path = "/api/health",
|
9
|
-
responses(
|
10
|
-
(status = 200, description = "Health check successful", body = ApiResponse<String>)
|
11
|
-
),
|
12
|
-
tag = "health"
|
13
|
-
)]
|
14
|
-
pub async fn health_check() -> Json<ApiResponse<String>> {
|
15
|
-
Json(ApiResponse::success("OK".to_string()))
|
16
|
-
}
|