diff --git a/docs/backend/architecture.md b/docs/backend/architecture.md index 72aa8494..3df48c2e 100644 --- a/docs/backend/architecture.md +++ b/docs/backend/architecture.md @@ -2,13 +2,14 @@ ## Goal -This structure keeps the Day 1 runtime stable while giving the backend a clear home for shared HTTP concerns, shared types, OpenAPI work, and feature modules. The current runtime still lives in `server/legacy-runtime.js`, but everything new should be shaped around the layout below. +This structure keeps the Day 1 runtime stable while giving the backend a clear home for shared HTTP concerns, shared types, OpenAPI work, and feature modules. The current runtime still lives in `server/index.js`, but everything new should be shaped around the layout below. ## Structure ```text server/ - legacy-runtime.js + index.js + start.js src/ app.ts bootstrap.ts @@ -51,17 +52,21 @@ server/ ## File And Folder Roles -- `server/legacy-runtime.js` +- `server/index.js` Temporary compatibility boundary for the old monolith. Day 1 keeps behavior here so the new TypeScript layout can grow around a stable runtime. Example: the existing websocket handlers, inline routes, and provider startup still live here until they are migrated module by module. +- `server/start.js` + Thin production entrypoint for the compiled backend output. + Example: `server:start` uses this file to verify `server/dist/bootstrap.js` exists and then loads it. + - `src/bootstrap.ts` Executable backend entrypoint used by `npm run server` and `npm run server:dev`. Example: `bootstrap.ts` should stay thin and do nothing except start the app, so later it remains safe to call in dev, prod, tests, or worker modes. - `src/app.ts` Composition root for the backend application. - Example: today it bridges into `legacy-runtime.js`; later it will create the Express app, apply shared middleware, register modules, attach websocket setup, and return the running application shape. + Example: today it bridges into `index.js`; later it will create the Express app, apply shared middleware, register modules, attach websocket setup, and return the running application shape. - `src/config/` Runtime configuration helpers and environment-aware path logic. @@ -177,7 +182,7 @@ server/ ## Day 1 Notes -- The runtime still executes through `server/legacy-runtime.js` for safety. +- The runtime still executes through `server/index.js` for safety. - The new `src/` structure is now the required home for all new backend code. - The generated inventory in `docs/backend/endpoint-inventory.*` is the source of truth for what must be migrated into these folders next. @@ -186,7 +191,7 @@ server/ These scripts live in `package.json`. The key distinction is: - `server` and `server:dev` run the backend directly from TypeScript. -- `server:start` runs the compiled backend through `server/index.js`. +- `server:start` runs the compiled backend through `server/start.js`. - `build` only builds the frontend. - `server:build` only builds the backend. - `start` runs the full production-style flow. @@ -199,12 +204,12 @@ These scripts live in `package.json`. The key distinction is: Example: you are editing a React screen that calls `/api/projects` and also changing the backend route behavior. - `npm run server:dev` - Starts the backend in watch mode with `tsx watch server/src/bootstrap.ts`. + Starts the backend in watch mode with `tsx watch --tsconfig server/tsconfig.json server/src/bootstrap.ts`. Use this for backend-only development. Example: you are refactoring request handling, logging, module structure, or shared HTTP utilities and want automatic restarts. - `npm run server` - Starts the backend once from TypeScript without watch mode. + Starts the backend once from TypeScript with `tsx --tsconfig server/tsconfig.json server/src/bootstrap.ts`. Use this when you want a stable one-shot backend process. Example: you want to reproduce a startup bug, inspect logs without reload noise, or test one backend flow manually. @@ -226,7 +231,7 @@ These scripts live in `package.json`. The key distinction is: Example: you changed `server/src/app.ts`, shared types, or future module imports and want to confirm compiled output is valid. - `npm run server:start` - Starts the built backend through `server/index.js`. + Starts the built backend through `server/start.js`. Use this after `npm run server:build` when you want to run compiled backend output only. Example: dev mode works, but you want to make sure the production entrypoint and compiled files also work correctly. diff --git a/docs/backend/endpoint-inventory.csv b/docs/backend/endpoint-inventory.csv index d2dedeb2..e2784b9f 100644 --- a/docs/backend/endpoint-inventory.csv +++ b/docs/backend/endpoint-inventory.csv @@ -1,34 +1,34 @@ transport,method,path,tag,authMode,sourceFile,sourceLine,purpose,consumerFiles,pathParams,queryParams,bodyHints,successShape,errorShape,sideEffects,priority -"http","GET","/health","System","public","server/legacy-runtime.js","345","Expose server health, timestamp, and install mode for diagnostics.","src/hooks/useVersionCheck.ts","","","","Structured JSON object response.","Handler-specific error behavior.","Read-only backend query.","low" -"http","POST","/api/system/update","System","bearer_token","server/legacy-runtime.js","425","Run the application update workflow on the host machine.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","","Structured JSON object response.","JSON error response with HTTP status code.","Mutates backend or external state.","low" -"http","GET","/api/projects","Projects","bearer_token","server/legacy-runtime.js","491","List detected projects and workspaces.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","","JSON payload returned directly from service logic.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","GET","/api/projects/:projectName/sessions","Sessions","bearer_token","server/legacy-runtime.js","500","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","offset; try { +"http","GET","/health","System","public","server/index.js","345","Expose server health, timestamp, and install mode for diagnostics.","src/hooks/useVersionCheck.ts","","","","Structured JSON object response.","Handler-specific error behavior.","Read-only backend query.","low" +"http","POST","/api/system/update","System","bearer_token","server/index.js","425","Run the application update workflow on the host machine.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","","Structured JSON object response.","JSON error response with HTTP status code.","Mutates backend or external state.","low" +"http","GET","/api/projects","Projects","bearer_token","server/index.js","491","List detected projects and workspaces.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","","JSON payload returned directly from service logic.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","GET","/api/projects/:projectName/sessions","Sessions","bearer_token","server/index.js","500","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","offset; try { const { limit","","JSON payload returned directly from service logic.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","GET","/api/projects/:projectName/sessions/:sessionId/messages","Sessions","bearer_token","server/legacy-runtime.js","512","Return paginated messages for a stored session.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","limit; offset","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","PUT","/api/projects/:projectName/rename","Projects","bearer_token","server/legacy-runtime.js","537","PUT /api/projects/:projectName/rename for backend runtime support.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","try { +"http","GET","/api/projects/:projectName/sessions/:sessionId/messages","Sessions","bearer_token","server/index.js","512","Return paginated messages for a stored session.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","limit; offset","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","PUT","/api/projects/:projectName/rename","Projects","bearer_token","server/index.js","537","PUT /api/projects/:projectName/rename for backend runtime support.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","try { const { displayName","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","DELETE","/api/projects/:projectName/sessions/:sessionId","Sessions","bearer_token","server/legacy-runtime.js","548","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","","","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","PUT","/api/sessions/:sessionId/rename","Sessions","bearer_token","server/legacy-runtime.js","563","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","sessionId","","provider; summary","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.","low" -"http","DELETE","/api/projects/:projectName","Projects","bearer_token","server/legacy-runtime.js","589","DELETE /api/projects/:projectName for backend runtime support.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","force","","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","POST","/api/projects/create","Projects","bearer_token","server/legacy-runtime.js","601","Manually add a project path to the workspace list.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","try { +"http","DELETE","/api/projects/:projectName/sessions/:sessionId","Sessions","bearer_token","server/index.js","548","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","","","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","PUT","/api/sessions/:sessionId/rename","Sessions","bearer_token","server/index.js","563","List or manage sessions associated with a project or provider.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","sessionId","","provider; summary","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.","low" +"http","DELETE","/api/projects/:projectName","Projects","bearer_token","server/index.js","589","DELETE /api/projects/:projectName for backend runtime support.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","force","","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","POST","/api/projects/create","Projects","bearer_token","server/index.js","601","Manually add a project path to the workspace list.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","try { const { path","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"sse","GET","/api/search/conversations","Sessions","bearer_token","server/legacy-runtime.js","618","Search conversation history across stored projects and stream results.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","limit; q","","Server-sent events stream with progress/result/error events.","Streamed error event or JSON error fallback.","Read-only backend query.","high" -"http","GET","/api/browse-filesystem","Realtime","bearer_token","server/legacy-runtime.js","674","Browse local directories so the UI can suggest workspace locations.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","try { +"sse","GET","/api/search/conversations","Sessions","bearer_token","server/index.js","618","Search conversation history across stored projects and stream results.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","limit; q","","Server-sent events stream with progress/result/error events.","Streamed error event or JSON error fallback.","Read-only backend query.","high" +"http","GET","/api/browse-filesystem","Realtime","bearer_token","server/index.js","674","Browse local directories so the UI can suggest workspace locations.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","try { const { path","","Structured JSON object response.","JSON object with error message and optional details.","Read-only backend query.","low" -"http","POST","/api/create-folder","Projects","bearer_token","server/legacy-runtime.js","754","Create a new directory on the local filesystem.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","try { +"http","POST","/api/create-folder","Projects","bearer_token","server/index.js","754","Create a new directory on the local filesystem.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","try { const { path","JSON object with an explicit success flag and payload.","JSON object with error message and optional details.","Mutates backend or external state.","low" -"http","GET","/api/projects/:projectName/file","Files","bearer_token","server/legacy-runtime.js","795","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","filePath","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","GET","/api/projects/:projectName/files/content","Files","bearer_token","server/legacy-runtime.js","835","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","path","","Mixed response shape; inspect handler during refactor.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","PUT","/api/projects/:projectName/file","Files","bearer_token","server/legacy-runtime.js","888","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","content; filePath","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","GET","/api/projects/:projectName/files","Files","bearer_token","server/legacy-runtime.js","937","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","JSON payload returned directly from service logic.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","POST","/api/projects/:projectName/files/create","Files","bearer_token","server/legacy-runtime.js","1016","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","name; path; type","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","PUT","/api/projects/:projectName/files/rename","Files","bearer_token","server/legacy-runtime.js","1093","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","newName; oldPath","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","DELETE","/api/projects/:projectName/files","Files","bearer_token","server/legacy-runtime.js","1170","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","path; relativePaths; targetPath; type","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","POST","/api/projects/:projectName/files/upload","Files","bearer_token","server/legacy-runtime.js","1396","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","Mixed response shape; inspect handler during refactor.","Handler-specific error behavior.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","POST","/api/transcribe","Realtime","bearer_token","server/legacy-runtime.js","1964","Transcribe uploaded audio and optionally enhance the result for prompts or tasks.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","mode","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Processes uploaded files and external model responses.","low" -"http","POST","/api/projects/:projectName/upload-images","Files","bearer_token","server/legacy-runtime.js","2113","Upload images for chat use and return browser-safe data URLs.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" -"http","GET","/api/projects/:projectName/sessions/:sessionId/token-usage","Sessions","bearer_token","server/legacy-runtime.js","2198","Report token usage for a stored provider session.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","provider","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" -"http","GET","*","System","public","server/legacy-runtime.js","2386","Serve the React application fallback for non-API routes.","","","","","Static file or HTML response.","JSON error response with HTTP status code.","Read-only backend query.","low" +"http","GET","/api/projects/:projectName/file","Files","bearer_token","server/index.js","795","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","filePath","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","GET","/api/projects/:projectName/files/content","Files","bearer_token","server/index.js","835","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","path","","Mixed response shape; inspect handler during refactor.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","PUT","/api/projects/:projectName/file","Files","bearer_token","server/index.js","888","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","content; filePath","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","GET","/api/projects/:projectName/files","Files","bearer_token","server/index.js","937","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","JSON payload returned directly from service logic.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","POST","/api/projects/:projectName/files/create","Files","bearer_token","server/index.js","1016","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","name; path; type","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","PUT","/api/projects/:projectName/files/rename","Files","bearer_token","server/index.js","1093","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","newName; oldPath","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","DELETE","/api/projects/:projectName/files","Files","bearer_token","server/index.js","1170","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","path; relativePaths; targetPath; type","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","POST","/api/projects/:projectName/files/upload","Files","bearer_token","server/index.js","1396","Read, write, create, rename, delete, or upload project files.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","Mixed response shape; inspect handler during refactor.","Handler-specific error behavior.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","POST","/api/transcribe","Realtime","bearer_token","server/index.js","1964","Transcribe uploaded audio and optionally enhance the result for prompts or tasks.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","mode","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Processes uploaded files and external model responses.","low" +"http","POST","/api/projects/:projectName/upload-images","Files","bearer_token","server/index.js","2113","Upload images for chat use and return browser-safe data URLs.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName","","","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Touches local workspace files or directories.","high" +"http","GET","/api/projects/:projectName/sessions/:sessionId/token-usage","Sessions","bearer_token","server/index.js","2198","Report token usage for a stored provider session.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","projectName; sessionId","provider","","Structured JSON object response.","JSON object with error message and optional details.","Touches local workspace files or directories.","high" +"http","GET","*","System","public","server/index.js","2386","Serve the React application fallback for non-API routes.","","","","","Static file or HTML response.","JSON error response with HTTP status code.","Read-only backend query.","low" "http","GET","/api/auth/status","Auth","public","server/routes/auth.js","9","Report whether authentication is configured.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","","Structured JSON object response.","JSON object with error message and optional details.","Reads or writes local authentication or credential state.","medium" "http","POST","/api/auth/register","Auth","public","server/routes/auth.js","23","Create the first local user account.","src/components/chat/hooks/useChatComposerState.ts; src/components/chat/hooks/useChatProviderState.ts; src/components/chat/hooks/useChatSessionState.ts; src/components/chat/hooks/useSlashCommands.ts; src/components/file-tree/view/ImageViewer.tsx; src/components/git-panel/hooks/useGitPanelController.ts; src/components/git-panel/hooks/useRevertLocalCommit.ts; src/components/onboarding/view/Onboarding.tsx; src/components/plugins/view/PluginIcon.tsx; src/components/plugins/view/PluginTabContent.tsx; src/components/prd-editor/hooks/usePrdSave.ts; src/components/project-creation-wizard/data/workspaceApi.ts; src/components/settings/constants/constants.ts; src/components/settings/hooks/useCredentialsSettings.ts; src/components/settings/hooks/useGitSettings.ts; src/components/settings/hooks/useSettingsController.ts; src/components/version-upgrade/view/VersionUpgradeModal.tsx; src/contexts/PluginsContext.tsx; src/utils/api.js","","","password; try { const { username","Structured JSON object response.","JSON object with error message and optional details.","Mutates backend or external state.; Reads or writes local authentication or credential state.","medium" diff --git a/docs/backend/endpoint-inventory.json b/docs/backend/endpoint-inventory.json index ea05c8ae..5dbd2147 100644 --- a/docs/backend/endpoint-inventory.json +++ b/docs/backend/endpoint-inventory.json @@ -41,7 +41,7 @@ "path": "/health", "tag": "System", "authMode": "public", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 345, "purpose": "Expose server health, timestamp, and install mode for diagnostics.", "consumerFiles": [ @@ -65,7 +65,7 @@ "path": "/api/system/update", "tag": "System", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 425, "purpose": "Run the application update workflow on the host machine.", "consumerFiles": [ @@ -107,7 +107,7 @@ "path": "/api/projects", "tag": "Projects", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 491, "purpose": "List detected projects and workspaces.", "consumerFiles": [ @@ -149,7 +149,7 @@ "path": "/api/projects/:projectName/sessions", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 500, "purpose": "List or manage sessions associated with a project or provider.", "consumerFiles": [ @@ -196,7 +196,7 @@ "path": "/api/projects/:projectName/sessions/:sessionId/messages", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 512, "purpose": "Return paginated messages for a stored session.", "consumerFiles": [ @@ -244,7 +244,7 @@ "path": "/api/projects/:projectName/rename", "tag": "Projects", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 537, "purpose": "PUT /api/projects/:projectName/rename for backend runtime support.", "consumerFiles": [ @@ -291,7 +291,7 @@ "path": "/api/projects/:projectName/sessions/:sessionId", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 548, "purpose": "List or manage sessions associated with a project or provider.", "consumerFiles": [ @@ -337,7 +337,7 @@ "path": "/api/sessions/:sessionId/rename", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 563, "purpose": "List or manage sessions associated with a project or provider.", "consumerFiles": [ @@ -384,7 +384,7 @@ "path": "/api/projects/:projectName", "tag": "Projects", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 589, "purpose": "DELETE /api/projects/:projectName for backend runtime support.", "consumerFiles": [ @@ -431,7 +431,7 @@ "path": "/api/projects/create", "tag": "Projects", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 601, "purpose": "Manually add a project path to the workspace list.", "consumerFiles": [ @@ -476,7 +476,7 @@ "path": "/api/search/conversations", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 618, "purpose": "Search conversation history across stored projects and stream results.", "consumerFiles": [ @@ -521,7 +521,7 @@ "path": "/api/browse-filesystem", "tag": "Realtime", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 674, "purpose": "Browse local directories so the UI can suggest workspace locations.", "consumerFiles": [ @@ -565,7 +565,7 @@ "path": "/api/create-folder", "tag": "Projects", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 754, "purpose": "Create a new directory on the local filesystem.", "consumerFiles": [ @@ -609,7 +609,7 @@ "path": "/api/projects/:projectName/file", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 795, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -655,7 +655,7 @@ "path": "/api/projects/:projectName/files/content", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 835, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -701,7 +701,7 @@ "path": "/api/projects/:projectName/file", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 888, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -749,7 +749,7 @@ "path": "/api/projects/:projectName/files", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 937, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -793,7 +793,7 @@ "path": "/api/projects/:projectName/files/create", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 1016, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -842,7 +842,7 @@ "path": "/api/projects/:projectName/files/rename", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 1093, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -890,7 +890,7 @@ "path": "/api/projects/:projectName/files", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 1170, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -940,7 +940,7 @@ "path": "/api/projects/:projectName/files/upload", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 1396, "purpose": "Read, write, create, rename, delete, or upload project files.", "consumerFiles": [ @@ -985,7 +985,7 @@ "path": "/api/transcribe", "tag": "Realtime", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 1964, "purpose": "Transcribe uploaded audio and optionally enhance the result for prompts or tasks.", "consumerFiles": [ @@ -1030,7 +1030,7 @@ "path": "/api/projects/:projectName/upload-images", "tag": "Files", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 2113, "purpose": "Upload images for chat use and return browser-safe data URLs.", "consumerFiles": [ @@ -1075,7 +1075,7 @@ "path": "/api/projects/:projectName/sessions/:sessionId/token-usage", "tag": "Sessions", "authMode": "bearer_token", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 2198, "purpose": "Report token usage for a stored provider session.", "consumerFiles": [ @@ -1122,7 +1122,7 @@ "path": "*", "tag": "System", "authMode": "public", - "sourceFile": "server/legacy-runtime.js", + "sourceFile": "server/index.js", "sourceLine": 2386, "purpose": "Serve the React application fallback for non-API routes.", "consumerFiles": [], @@ -5434,4 +5434,4 @@ "priority": "high" } ] -} \ No newline at end of file +} diff --git a/docs/backend/endpoint-inventory.md b/docs/backend/endpoint-inventory.md index 330a1476..a99b5921 100644 --- a/docs/backend/endpoint-inventory.md +++ b/docs/backend/endpoint-inventory.md @@ -52,15 +52,15 @@ Generated on 2026-03-11T17:31:18.119Z. | Method | Path | Auth | Purpose | Consumers | Source | | --- | --- | --- | --- | --- | --- | -| GET | `/api/projects/:projectName/file` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:795 | -| PUT | `/api/projects/:projectName/file` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:888 | -| GET | `/api/projects/:projectName/files` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:937 | -| DELETE | `/api/projects/:projectName/files` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:1170 | -| GET | `/api/projects/:projectName/files/content` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:835 | -| POST | `/api/projects/:projectName/files/create` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:1016 | -| PUT | `/api/projects/:projectName/files/rename` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:1093 | -| POST | `/api/projects/:projectName/files/upload` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:1396 | -| POST | `/api/projects/:projectName/upload-images` | bearer_token | Upload images for chat use and return browser-safe data URLs. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:2113 | +| GET | `/api/projects/:projectName/file` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:795 | +| PUT | `/api/projects/:projectName/file` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:888 | +| GET | `/api/projects/:projectName/files` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:937 | +| DELETE | `/api/projects/:projectName/files` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:1170 | +| GET | `/api/projects/:projectName/files/content` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:835 | +| POST | `/api/projects/:projectName/files/create` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:1016 | +| PUT | `/api/projects/:projectName/files/rename` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:1093 | +| POST | `/api/projects/:projectName/files/upload` | bearer_token | Read, write, create, rename, delete, or upload project files. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:1396 | +| POST | `/api/projects/:projectName/upload-images` | bearer_token | Upload images for chat use and return browser-safe data URLs. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:2113 | ## Git @@ -115,12 +115,12 @@ Generated on 2026-03-11T17:31:18.119Z. | Method | Path | Auth | Purpose | Consumers | Source | | --- | --- | --- | --- | --- | --- | -| POST | `/api/create-folder` | bearer_token | Create a new directory on the local filesystem. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:754 | -| GET | `/api/projects` | bearer_token | List detected projects and workspaces. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:491 | -| DELETE | `/api/projects/:projectName` | bearer_token | DELETE /api/projects/:projectName for backend runtime support. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:589 | -| PUT | `/api/projects/:projectName/rename` | bearer_token | PUT /api/projects/:projectName/rename for backend runtime support. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:537 | +| POST | `/api/create-folder` | bearer_token | Create a new directory on the local filesystem. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:754 | +| GET | `/api/projects` | bearer_token | List detected projects and workspaces. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:491 | +| DELETE | `/api/projects/:projectName` | bearer_token | DELETE /api/projects/:projectName for backend runtime support. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:589 | +| PUT | `/api/projects/:projectName/rename` | bearer_token | PUT /api/projects/:projectName/rename for backend runtime support. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:537 | | GET | `/api/projects/clone-progress` | bearer_token | Stream workspace cloning progress events to the frontend. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/routes/projects.js:335 | -| POST | `/api/projects/create` | bearer_token | Manually add a project path to the workspace list. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:601 | +| POST | `/api/projects/create` | bearer_token | Manually add a project path to the workspace list. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:601 | | POST | `/api/projects/create-workspace` | bearer_token | Create or register a workspace and optionally clone a GitHub repository into it. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/routes/projects.js:175 | ## Providers @@ -151,19 +151,19 @@ Generated on 2026-03-11T17:31:18.119Z. | Method | Path | Auth | Purpose | Consumers | Source | | --- | --- | --- | --- | --- | --- | -| GET | `/api/browse-filesystem` | bearer_token | Browse local directories so the UI can suggest workspace locations. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:674 | -| POST | `/api/transcribe` | bearer_token | Transcribe uploaded audio and optionally enhance the result for prompts or tasks. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:1964 | +| GET | `/api/browse-filesystem` | bearer_token | Browse local directories so the UI can suggest workspace locations. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:674 | +| POST | `/api/transcribe` | bearer_token | Transcribe uploaded audio and optionally enhance the result for prompts or tasks. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:1964 | ## Sessions | Method | Path | Auth | Purpose | Consumers | Source | | --- | --- | --- | --- | --- | --- | -| GET | `/api/projects/:projectName/sessions` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:500 | -| DELETE | `/api/projects/:projectName/sessions/:sessionId` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:548 | -| GET | `/api/projects/:projectName/sessions/:sessionId/messages` | bearer_token | Return paginated messages for a stored session. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:512 | -| GET | `/api/projects/:projectName/sessions/:sessionId/token-usage` | bearer_token | Report token usage for a stored provider session. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:2198 | -| GET | `/api/search/conversations` | bearer_token | Search conversation history across stored projects and stream results. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:618 | -| PUT | `/api/sessions/:sessionId/rename` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:563 | +| GET | `/api/projects/:projectName/sessions` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:500 | +| DELETE | `/api/projects/:projectName/sessions/:sessionId` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:548 | +| GET | `/api/projects/:projectName/sessions/:sessionId/messages` | bearer_token | Return paginated messages for a stored session. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:512 | +| GET | `/api/projects/:projectName/sessions/:sessionId/token-usage` | bearer_token | Report token usage for a stored provider session. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:2198 | +| GET | `/api/search/conversations` | bearer_token | Search conversation history across stored projects and stream results. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:618 | +| PUT | `/api/sessions/:sessionId/rename` | bearer_token | List or manage sessions associated with a project or provider. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:563 | ## Settings @@ -182,9 +182,9 @@ Generated on 2026-03-11T17:31:18.119Z. | Method | Path | Auth | Purpose | Consumers | Source | | --- | --- | --- | --- | --- | --- | -| GET | `*` | public | Serve the React application fallback for non-API routes. | - | server/legacy-runtime.js:2386 | -| POST | `/api/system/update` | bearer_token | Run the application update workflow on the host machine. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/legacy-runtime.js:425 | -| GET | `/health` | public | Expose server health, timestamp, and install mode for diagnostics. | src/hooks/useVersionCheck.ts | server/legacy-runtime.js:345 | +| GET | `*` | public | Serve the React application fallback for non-API routes. | - | server/index.js:2386 | +| POST | `/api/system/update` | bearer_token | Run the application update workflow on the host machine. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/index.js:425 | +| GET | `/health` | public | Expose server health, timestamp, and install mode for diagnostics. | src/hooks/useVersionCheck.ts | server/index.js:345 | ## TaskMaster @@ -215,4 +215,3 @@ Generated on 2026-03-11T17:31:18.119Z. | GET | `/api/user/git-config` | bearer_token | Read or update stored git identity settings. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/routes/user.js:28 | | POST | `/api/user/git-config` | bearer_token | Read or update stored git identity settings. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/routes/user.js:57 | | GET | `/api/user/onboarding-status` | bearer_token | Return onboarding completion status for the current user. | src/components/chat/hooks/useChatComposerState.ts
src/components/chat/hooks/useChatProviderState.ts
src/components/chat/hooks/useChatSessionState.ts
src/components/chat/hooks/useSlashCommands.ts
src/components/file-tree/view/ImageViewer.tsx
src/components/git-panel/hooks/useGitPanelController.ts
src/components/git-panel/hooks/useRevertLocalCommit.ts
src/components/onboarding/view/Onboarding.tsx
src/components/plugins/view/PluginIcon.tsx
src/components/plugins/view/PluginTabContent.tsx
src/components/prd-editor/hooks/usePrdSave.ts
src/components/project-creation-wizard/data/workspaceApi.ts
src/components/settings/constants/constants.ts
src/components/settings/hooks/useCredentialsSettings.ts
src/components/settings/hooks/useGitSettings.ts
src/components/settings/hooks/useSettingsController.ts
src/components/version-upgrade/view/VersionUpgradeModal.tsx
src/contexts/PluginsContext.tsx
src/utils/api.js | server/routes/user.js:108 | - diff --git a/package.json b/package.json index 8041afc4..211befaa 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.26.3", "description": "A web-based UI for Claude Code CLI", "type": "module", - "main": "server/index.js", + "main": "server/start.js", "bin": { "claude-code-ui": "server/cli.js", "cloudcli": "server/cli.js" @@ -25,10 +25,10 @@ }, "scripts": { "dev": "concurrently --kill-others \"npm run server\" \"npm run client\"", - "server:dev": "tsx watch server/src/bootstrap.ts", - "server": "tsx server/src/bootstrap.ts", + "server:dev": "tsx watch --tsconfig server/tsconfig.json server/src/bootstrap.ts", + "server": "tsx --tsconfig server/tsconfig.json server/src/bootstrap.ts", "server:build": "tsc -p server/tsconfig.json && tsc-alias -p server/tsconfig.json", - "server:start": "node server/index.js", + "server:start": "node server/start.js", "client": "vite", "build": "vite build", "preview": "vite preview", diff --git a/scripts/generate-backend-inventory.mjs b/scripts/generate-backend-inventory.mjs index 47b985ba..5b4d6e03 100644 --- a/scripts/generate-backend-inventory.mjs +++ b/scripts/generate-backend-inventory.mjs @@ -620,7 +620,7 @@ function writeMarkdown(filePath, summary, records, realtimeContracts) { } const clientFiles = walkFiles(clientRoot).filter(filePath => /\.(js|jsx|ts|tsx)$/.test(filePath)); -const legacyRuntimePath = path.join(serverRoot, 'legacy-runtime.js'); +const legacyRuntimePath = path.join(serverRoot, 'index.js'); const runtimeContent = readText(legacyRuntimePath); const mounts = parseMounts(runtimeContent); const records = []; @@ -639,7 +639,7 @@ const summary = { httpRoutes: records.filter(record => record.transport === 'http').length, sseRoutes: records.filter(record => record.transport === 'sse').length, modularRoutes: records.filter(record => record.sourceFile.includes('/routes/')).length, - inlineRoutes: records.filter(record => record.sourceFile === 'server/legacy-runtime.js').length, + inlineRoutes: records.filter(record => record.sourceFile === 'server/index.js').length, routeFilesScanned: new Set(records.map(record => record.sourceFile)).size, }; diff --git a/server/index.js b/server/index.js index f46e87f5..5318fb35 100755 --- a/server/index.js +++ b/server/index.js @@ -1,11 +1,13 @@ #!/usr/bin/env node +// Load environment variables before other imports execute +import './load-env.js'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; +import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); -const __dirname = path.dirname(__filename); -const distEntrypoint = path.join(__dirname, 'dist', 'bootstrap.js'); +const __dirname = dirname(__filename); const installMode = fs.existsSync(path.join(__dirname, '..', '.git')) ? 'git' : 'npm'; diff --git a/server/legacy-runtime.js b/server/legacy-runtime.js deleted file mode 100644 index 770575a2..00000000 --- a/server/legacy-runtime.js +++ /dev/null @@ -1,2554 +0,0 @@ -#!/usr/bin/env node -// Load environment variables before other imports execute -import './load-env.js'; -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; -import { dirname } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -const installMode = fs.existsSync(path.join(__dirname, '..', '.git')) ? 'git' : 'npm'; - -// ANSI color codes for terminal output -const colors = { - reset: '\x1b[0m', - bright: '\x1b[1m', - cyan: '\x1b[36m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - dim: '\x1b[2m', -}; - -const c = { - info: (text) => `${colors.cyan}${text}${colors.reset}`, - ok: (text) => `${colors.green}${text}${colors.reset}`, - warn: (text) => `${colors.yellow}${text}${colors.reset}`, - tip: (text) => `${colors.blue}${text}${colors.reset}`, - bright: (text) => `${colors.bright}${text}${colors.reset}`, - dim: (text) => `${colors.dim}${text}${colors.reset}`, -}; - -console.log('SERVER_PORT from env:', process.env.SERVER_PORT); - -import express from 'express'; -import { WebSocketServer, WebSocket } from 'ws'; -import os from 'os'; -import http from 'http'; -import cors from 'cors'; -import { promises as fsPromises } from 'fs'; -import { spawn } from 'child_process'; -import pty from 'node-pty'; -import fetch from 'node-fetch'; -import mime from 'mime-types'; - -import { getProjects, getSessions, getSessionMessages, renameProject, deleteSession, deleteProject, addProjectManually, extractProjectDirectory, clearProjectDirectoryCache, searchConversations } from './projects.js'; -import { queryClaudeSDK, abortClaudeSDKSession, isClaudeSDKSessionActive, getActiveClaudeSDKSessions, resolveToolApproval, getPendingApprovalsForSession, reconnectSessionWriter } from './claude-sdk.js'; -import { spawnCursor, abortCursorSession, isCursorSessionActive, getActiveCursorSessions } from './cursor-cli.js'; -import { queryCodex, abortCodexSession, isCodexSessionActive, getActiveCodexSessions } from './openai-codex.js'; -import { spawnGemini, abortGeminiSession, isGeminiSessionActive, getActiveGeminiSessions } from './gemini-cli.js'; -import sessionManager from './sessionManager.js'; -import gitRoutes from './routes/git.js'; -import authRoutes from './routes/auth.js'; -import mcpRoutes from './routes/mcp.js'; -import cursorRoutes from './routes/cursor.js'; -import taskmasterRoutes from './routes/taskmaster.js'; -import mcpUtilsRoutes from './routes/mcp-utils.js'; -import commandsRoutes from './routes/commands.js'; -import settingsRoutes from './routes/settings.js'; -import agentRoutes from './routes/agent.js'; -import projectsRoutes, { WORKSPACES_ROOT, validateWorkspacePath } from './routes/projects.js'; -import cliAuthRoutes from './routes/cli-auth.js'; -import userRoutes from './routes/user.js'; -import codexRoutes from './routes/codex.js'; -import geminiRoutes from './routes/gemini.js'; -import pluginsRoutes from './routes/plugins.js'; -import { startEnabledPluginServers, stopAllPlugins } from './utils/plugin-process-manager.js'; -import { initializeDatabase, sessionNamesDb, applyCustomSessionNames } from './database/db.js'; -import { validateApiKey, authenticateToken, authenticateWebSocket } from './middleware/auth.js'; -import { IS_PLATFORM } from './constants/config.js'; -import { getConnectableHost } from '../shared/networkHosts.js'; - -const VALID_PROVIDERS = ['claude', 'codex', 'cursor', 'gemini']; - -// File system watchers for provider project/session folders -const PROVIDER_WATCH_PATHS = [ - { provider: 'claude', rootPath: path.join(os.homedir(), '.claude', 'projects') }, - { provider: 'cursor', rootPath: path.join(os.homedir(), '.cursor', 'chats') }, - { provider: 'codex', rootPath: path.join(os.homedir(), '.codex', 'sessions') }, - { provider: 'gemini', rootPath: path.join(os.homedir(), '.gemini', 'projects') }, - { provider: 'gemini_sessions', rootPath: path.join(os.homedir(), '.gemini', 'sessions') } -]; -const WATCHER_IGNORED_PATTERNS = [ - '**/node_modules/**', - '**/.git/**', - '**/dist/**', - '**/build/**', - '**/*.tmp', - '**/*.swp', - '**/.DS_Store' -]; -const WATCHER_DEBOUNCE_MS = 300; -let projectsWatchers = []; -let projectsWatcherDebounceTimer = null; -const connectedClients = new Set(); -let isGetProjectsRunning = false; // Flag to prevent reentrant calls - -// Broadcast progress to all connected WebSocket clients -function broadcastProgress(progress) { - const message = JSON.stringify({ - type: 'loading_progress', - ...progress - }); - connectedClients.forEach(client => { - if (client.readyState === WebSocket.OPEN) { - client.send(message); - } - }); -} - -// Setup file system watchers for Claude, Cursor, and Codex project/session folders -async function setupProjectsWatcher() { - const chokidar = (await import('chokidar')).default; - - if (projectsWatcherDebounceTimer) { - clearTimeout(projectsWatcherDebounceTimer); - projectsWatcherDebounceTimer = null; - } - - await Promise.all( - projectsWatchers.map(async (watcher) => { - try { - await watcher.close(); - } catch (error) { - console.error('[WARN] Failed to close watcher:', error); - } - }) - ); - projectsWatchers = []; - - const debouncedUpdate = (eventType, filePath, provider, rootPath) => { - if (projectsWatcherDebounceTimer) { - clearTimeout(projectsWatcherDebounceTimer); - } - - projectsWatcherDebounceTimer = setTimeout(async () => { - // Prevent reentrant calls - if (isGetProjectsRunning) { - return; - } - - try { - isGetProjectsRunning = true; - - // Clear project directory cache when files change - clearProjectDirectoryCache(); - - // Get updated projects list - const updatedProjects = await getProjects(broadcastProgress); - - // Notify all connected clients about the project changes - const updateMessage = JSON.stringify({ - type: 'projects_updated', - projects: updatedProjects, - timestamp: new Date().toISOString(), - changeType: eventType, - changedFile: path.relative(rootPath, filePath), - watchProvider: provider - }); - - connectedClients.forEach(client => { - if (client.readyState === WebSocket.OPEN) { - client.send(updateMessage); - } - }); - - } catch (error) { - console.error('[ERROR] Error handling project changes:', error); - } finally { - isGetProjectsRunning = false; - } - }, WATCHER_DEBOUNCE_MS); - }; - - for (const { provider, rootPath } of PROVIDER_WATCH_PATHS) { - try { - // chokidar v4 emits ENOENT via the "error" event for missing roots and will not auto-recover. - // Ensure provider folders exist before creating the watcher so watching stays active. - await fsPromises.mkdir(rootPath, { recursive: true }); - - // Initialize chokidar watcher with optimized settings - const watcher = chokidar.watch(rootPath, { - ignored: WATCHER_IGNORED_PATTERNS, - persistent: true, - ignoreInitial: true, // Don't fire events for existing files on startup - followSymlinks: false, - depth: 10, // Reasonable depth limit - awaitWriteFinish: { - stabilityThreshold: 100, // Wait 100ms for file to stabilize - pollInterval: 50 - } - }); - - // Set up event listeners - watcher - .on('add', (filePath) => debouncedUpdate('add', filePath, provider, rootPath)) - .on('change', (filePath) => debouncedUpdate('change', filePath, provider, rootPath)) - .on('unlink', (filePath) => debouncedUpdate('unlink', filePath, provider, rootPath)) - .on('addDir', (dirPath) => debouncedUpdate('addDir', dirPath, provider, rootPath)) - .on('unlinkDir', (dirPath) => debouncedUpdate('unlinkDir', dirPath, provider, rootPath)) - .on('error', (error) => { - console.error(`[ERROR] ${provider} watcher error:`, error); - }) - .on('ready', () => { - }); - - projectsWatchers.push(watcher); - } catch (error) { - console.error(`[ERROR] Failed to setup ${provider} watcher for ${rootPath}:`, error); - } - } - - if (projectsWatchers.length === 0) { - console.error('[ERROR] Failed to setup any provider watchers'); - } -} - - -const app = express(); -const server = http.createServer(app); - -const ptySessionsMap = new Map(); -const PTY_SESSION_TIMEOUT = 30 * 60 * 1000; -const SHELL_URL_PARSE_BUFFER_LIMIT = 32768; -const ANSI_ESCAPE_SEQUENCE_REGEX = /\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\))/g; -const TRAILING_URL_PUNCTUATION_REGEX = /[)\]}>.,;:!?]+$/; - -function stripAnsiSequences(value = '') { - return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, ''); -} - -function normalizeDetectedUrl(url) { - if (!url || typeof url !== 'string') return null; - - const cleaned = url.trim().replace(TRAILING_URL_PUNCTUATION_REGEX, ''); - if (!cleaned) return null; - - try { - const parsed = new URL(cleaned); - if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { - return null; - } - return parsed.toString(); - } catch { - return null; - } -} - -function extractUrlsFromText(value = '') { - const directMatches = value.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/gi) || []; - - // Handle wrapped terminal URLs split across lines by terminal width. - const wrappedMatches = []; - const continuationRegex = /^[A-Za-z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$/; - const lines = value.split(/\r?\n/); - for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim(); - const startMatch = line.match(/https?:\/\/[^\s<>"'`\\\x1b\x07]+/i); - if (!startMatch) continue; - - let combined = startMatch[0]; - let j = i + 1; - while (j < lines.length) { - const continuation = lines[j].trim(); - if (!continuation) break; - if (!continuationRegex.test(continuation)) break; - combined += continuation; - j++; - } - - wrappedMatches.push(combined.replace(/\r?\n\s*/g, '')); - } - - return Array.from(new Set([...directMatches, ...wrappedMatches])); -} - -function shouldAutoOpenUrlFromOutput(value = '') { - const normalized = value.toLowerCase(); - return ( - normalized.includes('browser didn\'t open') || - normalized.includes('open this url') || - normalized.includes('continue in your browser') || - normalized.includes('press enter to open') || - normalized.includes('open_url:') - ); -} - -// Single WebSocket server that handles both paths -const wss = new WebSocketServer({ - server, - verifyClient: (info) => { - console.log('WebSocket connection attempt to:', info.req.url); - - // Platform mode: always allow connection - if (IS_PLATFORM) { - const user = authenticateWebSocket(null); // Will return first user - if (!user) { - console.log('[WARN] Platform mode: No user found in database'); - return false; - } - info.req.user = user; - console.log('[OK] Platform mode WebSocket authenticated for user:', user.username); - return true; - } - - // Normal mode: verify token - // Extract token from query parameters or headers - const url = new URL(info.req.url, 'http://localhost'); - const token = url.searchParams.get('token') || - info.req.headers.authorization?.split(' ')[1]; - - // Verify token - const user = authenticateWebSocket(token); - if (!user) { - console.log('[WARN] WebSocket authentication failed'); - return false; - } - - // Store user info in the request for later use - info.req.user = user; - console.log('[OK] WebSocket authenticated for user:', user.username); - return true; - } -}); - -// Make WebSocket server available to routes -app.locals.wss = wss; - -app.use(cors({ exposedHeaders: ['X-Refreshed-Token'] })); -app.use(express.json({ - limit: '50mb', - type: (req) => { - // Skip multipart/form-data requests (for file uploads like images) - const contentType = req.headers['content-type'] || ''; - if (contentType.includes('multipart/form-data')) { - return false; - } - return contentType.includes('json'); - } -})); -app.use(express.urlencoded({ limit: '50mb', extended: true })); - -// Public health check endpoint (no authentication required) -app.get('/health', (req, res) => { - res.json({ - status: 'ok', - timestamp: new Date().toISOString(), - installMode - }); -}); - -// Optional API key validation (if configured) -app.use('/api', validateApiKey); - -// Authentication routes (public) -app.use('/api/auth', authRoutes); - -// Projects API Routes (protected) -app.use('/api/projects', authenticateToken, projectsRoutes); - -// Git API Routes (protected) -app.use('/api/git', authenticateToken, gitRoutes); - -// MCP API Routes (protected) -app.use('/api/mcp', authenticateToken, mcpRoutes); - -// Cursor API Routes (protected) -app.use('/api/cursor', authenticateToken, cursorRoutes); - -// TaskMaster API Routes (protected) -app.use('/api/taskmaster', authenticateToken, taskmasterRoutes); - -// MCP utilities -app.use('/api/mcp-utils', authenticateToken, mcpUtilsRoutes); - -// Commands API Routes (protected) -app.use('/api/commands', authenticateToken, commandsRoutes); - -// Settings API Routes (protected) -app.use('/api/settings', authenticateToken, settingsRoutes); - -// CLI Authentication API Routes (protected) -app.use('/api/cli', authenticateToken, cliAuthRoutes); - -// User API Routes (protected) -app.use('/api/user', authenticateToken, userRoutes); - -// Codex API Routes (protected) -app.use('/api/codex', authenticateToken, codexRoutes); - -// Gemini API Routes (protected) -app.use('/api/gemini', authenticateToken, geminiRoutes); - -// Plugins API Routes (protected) -app.use('/api/plugins', authenticateToken, pluginsRoutes); - -// Agent API Routes (uses API key authentication) -app.use('/api/agent', agentRoutes); - -// Serve public files (like api-docs.html) -app.use(express.static(path.join(__dirname, '../public'))); - -// Static files served after API routes -// Add cache control: HTML files should not be cached, but assets can be cached -app.use(express.static(path.join(__dirname, '../dist'), { - setHeaders: (res, filePath) => { - if (filePath.endsWith('.html')) { - // Prevent HTML caching to avoid service worker issues after builds - res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); - res.setHeader('Pragma', 'no-cache'); - res.setHeader('Expires', '0'); - } else if (filePath.match(/\.(js|css|woff2?|ttf|eot|svg|png|jpg|jpeg|gif|ico)$/)) { - // Cache static assets for 1 year (they have hashed names) - res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); - } - } -})); - -// API Routes (protected) -// /api/config endpoint removed - no longer needed -// Frontend now uses window.location for WebSocket URLs - -// System update endpoint -app.post('/api/system/update', authenticateToken, async (req, res) => { - try { - // Get the project root directory (parent of server directory) - const projectRoot = path.join(__dirname, '..'); - - console.log('Starting system update from directory:', projectRoot); - - // Run the update command based on install mode - const updateCommand = installMode === 'git' - ? 'git checkout main && git pull && npm install' - : 'npm install -g @siteboon/claude-code-ui@latest'; - - const child = spawn('sh', ['-c', updateCommand], { - cwd: installMode === 'git' ? projectRoot : os.homedir(), - env: process.env - }); - - let output = ''; - let errorOutput = ''; - - child.stdout.on('data', (data) => { - const text = data.toString(); - output += text; - console.log('Update output:', text); - }); - - child.stderr.on('data', (data) => { - const text = data.toString(); - errorOutput += text; - console.error('Update error:', text); - }); - - child.on('close', (code) => { - if (code === 0) { - res.json({ - success: true, - output: output || 'Update completed successfully', - message: 'Update completed. Please restart the server to apply changes.' - }); - } else { - res.status(500).json({ - success: false, - error: 'Update command failed', - output: output, - errorOutput: errorOutput - }); - } - }); - - child.on('error', (error) => { - console.error('Update process error:', error); - res.status(500).json({ - success: false, - error: error.message - }); - }); - - } catch (error) { - console.error('System update error:', error); - res.status(500).json({ - success: false, - error: error.message - }); - } -}); - -app.get('/api/projects', authenticateToken, async (req, res) => { - try { - const projects = await getProjects(broadcastProgress); - res.json(projects); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -app.get('/api/projects/:projectName/sessions', authenticateToken, async (req, res) => { - try { - const { limit = 5, offset = 0 } = req.query; - const result = await getSessions(req.params.projectName, parseInt(limit), parseInt(offset)); - applyCustomSessionNames(result.sessions, 'claude'); - res.json(result); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -// Get messages for a specific session -app.get('/api/projects/:projectName/sessions/:sessionId/messages', authenticateToken, async (req, res) => { - try { - const { projectName, sessionId } = req.params; - const { limit, offset } = req.query; - - // Parse limit and offset if provided - const parsedLimit = limit ? parseInt(limit, 10) : null; - const parsedOffset = offset ? parseInt(offset, 10) : 0; - - const result = await getSessionMessages(projectName, sessionId, parsedLimit, parsedOffset); - - // Handle both old and new response formats - if (Array.isArray(result)) { - // Backward compatibility: no pagination parameters were provided - res.json({ messages: result }); - } else { - // New format with pagination info - res.json(result); - } - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -// Rename project endpoint -app.put('/api/projects/:projectName/rename', authenticateToken, async (req, res) => { - try { - const { displayName } = req.body; - await renameProject(req.params.projectName, displayName); - res.json({ success: true }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -// Delete session endpoint -app.delete('/api/projects/:projectName/sessions/:sessionId', authenticateToken, async (req, res) => { - try { - const { projectName, sessionId } = req.params; - console.log(`[API] Deleting session: ${sessionId} from project: ${projectName}`); - await deleteSession(projectName, sessionId); - sessionNamesDb.deleteName(sessionId, 'claude'); - console.log(`[API] Session ${sessionId} deleted successfully`); - res.json({ success: true }); - } catch (error) { - console.error(`[API] Error deleting session ${req.params.sessionId}:`, error); - res.status(500).json({ error: error.message }); - } -}); - -// Rename session endpoint -app.put('/api/sessions/:sessionId/rename', authenticateToken, async (req, res) => { - try { - const { sessionId } = req.params; - const safeSessionId = String(sessionId).replace(/[^a-zA-Z0-9._-]/g, ''); - if (!safeSessionId || safeSessionId !== String(sessionId)) { - return res.status(400).json({ error: 'Invalid sessionId' }); - } - const { summary, provider } = req.body; - if (!summary || typeof summary !== 'string' || summary.trim() === '') { - return res.status(400).json({ error: 'Summary is required' }); - } - if (summary.trim().length > 500) { - return res.status(400).json({ error: 'Summary must not exceed 500 characters' }); - } - if (!provider || !VALID_PROVIDERS.includes(provider)) { - return res.status(400).json({ error: `Provider must be one of: ${VALID_PROVIDERS.join(', ')}` }); - } - sessionNamesDb.setName(safeSessionId, provider, summary.trim()); - res.json({ success: true }); - } catch (error) { - console.error(`[API] Error renaming session ${req.params.sessionId}:`, error); - res.status(500).json({ error: error.message }); - } -}); - -// Delete project endpoint (force=true to delete with sessions) -app.delete('/api/projects/:projectName', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const force = req.query.force === 'true'; - await deleteProject(projectName, force); - res.json({ success: true }); - } catch (error) { - res.status(500).json({ error: error.message }); - } -}); - -// Create project endpoint -app.post('/api/projects/create', authenticateToken, async (req, res) => { - try { - const { path: projectPath } = req.body; - - if (!projectPath || !projectPath.trim()) { - return res.status(400).json({ error: 'Project path is required' }); - } - - const project = await addProjectManually(projectPath.trim()); - res.json({ success: true, project }); - } catch (error) { - console.error('Error creating project:', error); - res.status(500).json({ error: error.message }); - } -}); - -// Search conversations content (SSE streaming) -app.get('/api/search/conversations', authenticateToken, async (req, res) => { - const query = typeof req.query.q === 'string' ? req.query.q.trim() : ''; - const parsedLimit = Number.parseInt(String(req.query.limit), 10); - const limit = Number.isNaN(parsedLimit) ? 50 : Math.max(1, Math.min(parsedLimit, 100)); - - if (query.length < 2) { - return res.status(400).json({ error: 'Query must be at least 2 characters' }); - } - - res.writeHead(200, { - 'Content-Type': 'text/event-stream', - 'Cache-Control': 'no-cache', - 'Connection': 'keep-alive', - 'X-Accel-Buffering': 'no', - }); - - let closed = false; - const abortController = new AbortController(); - req.on('close', () => { closed = true; abortController.abort(); }); - - try { - await searchConversations(query, limit, ({ projectResult, totalMatches, scannedProjects, totalProjects }) => { - if (closed) return; - if (projectResult) { - res.write(`event: result\ndata: ${JSON.stringify({ projectResult, totalMatches, scannedProjects, totalProjects })}\n\n`); - } else { - res.write(`event: progress\ndata: ${JSON.stringify({ totalMatches, scannedProjects, totalProjects })}\n\n`); - } - }, abortController.signal); - if (!closed) { - res.write(`event: done\ndata: {}\n\n`); - } - } catch (error) { - console.error('Error searching conversations:', error); - if (!closed) { - res.write(`event: error\ndata: ${JSON.stringify({ error: 'Search failed' })}\n\n`); - } - } finally { - if (!closed) { - res.end(); - } - } -}); - -const expandWorkspacePath = (inputPath) => { - if (!inputPath) return inputPath; - if (inputPath === '~') { - return WORKSPACES_ROOT; - } - if (inputPath.startsWith('~/') || inputPath.startsWith('~\\')) { - return path.join(WORKSPACES_ROOT, inputPath.slice(2)); - } - return inputPath; -}; - -// Browse filesystem endpoint for project suggestions - uses existing getFileTree -app.get('/api/browse-filesystem', authenticateToken, async (req, res) => { - try { - const { path: dirPath } = req.query; - - console.log('[API] Browse filesystem request for path:', dirPath); - console.log('[API] WORKSPACES_ROOT is:', WORKSPACES_ROOT); - // Default to home directory if no path provided - const defaultRoot = WORKSPACES_ROOT; - let targetPath = dirPath ? expandWorkspacePath(dirPath) : defaultRoot; - - // Resolve and normalize the path - targetPath = path.resolve(targetPath); - - // Security check - ensure path is within allowed workspace root - const validation = await validateWorkspacePath(targetPath); - if (!validation.valid) { - return res.status(403).json({ error: validation.error }); - } - const resolvedPath = validation.resolvedPath || targetPath; - - // Security check - ensure path is accessible - try { - await fs.promises.access(resolvedPath); - const stats = await fs.promises.stat(resolvedPath); - - if (!stats.isDirectory()) { - return res.status(400).json({ error: 'Path is not a directory' }); - } - } catch (err) { - return res.status(404).json({ error: 'Directory not accessible' }); - } - - // Use existing getFileTree function with shallow depth (only direct children) - const fileTree = await getFileTree(resolvedPath, 1, 0, false); // maxDepth=1, showHidden=false - - // Filter only directories and format for suggestions - const directories = fileTree - .filter(item => item.type === 'directory') - .map(item => ({ - path: item.path, - name: item.name, - type: 'directory' - })) - .sort((a, b) => { - const aHidden = a.name.startsWith('.'); - const bHidden = b.name.startsWith('.'); - if (aHidden && !bHidden) return 1; - if (!aHidden && bHidden) return -1; - return a.name.localeCompare(b.name); - }); - - // Add common directories if browsing home directory - const suggestions = []; - let resolvedWorkspaceRoot = defaultRoot; - try { - resolvedWorkspaceRoot = await fsPromises.realpath(defaultRoot); - } catch (error) { - // Use default root as-is if realpath fails - } - if (resolvedPath === resolvedWorkspaceRoot) { - const commonDirs = ['Desktop', 'Documents', 'Projects', 'Development', 'Dev', 'Code', 'workspace']; - const existingCommon = directories.filter(dir => commonDirs.includes(dir.name)); - const otherDirs = directories.filter(dir => !commonDirs.includes(dir.name)); - - suggestions.push(...existingCommon, ...otherDirs); - } else { - suggestions.push(...directories); - } - - res.json({ - path: resolvedPath, - suggestions: suggestions - }); - - } catch (error) { - console.error('Error browsing filesystem:', error); - res.status(500).json({ error: 'Failed to browse filesystem' }); - } -}); - -app.post('/api/create-folder', authenticateToken, async (req, res) => { - try { - const { path: folderPath } = req.body; - if (!folderPath) { - return res.status(400).json({ error: 'Path is required' }); - } - const expandedPath = expandWorkspacePath(folderPath); - const resolvedInput = path.resolve(expandedPath); - const validation = await validateWorkspacePath(resolvedInput); - if (!validation.valid) { - return res.status(403).json({ error: validation.error }); - } - const targetPath = validation.resolvedPath || resolvedInput; - const parentDir = path.dirname(targetPath); - try { - await fs.promises.access(parentDir); - } catch (err) { - return res.status(404).json({ error: 'Parent directory does not exist' }); - } - try { - await fs.promises.access(targetPath); - return res.status(409).json({ error: 'Folder already exists' }); - } catch (err) { - // Folder doesn't exist, which is what we want - } - try { - await fs.promises.mkdir(targetPath, { recursive: false }); - res.json({ success: true, path: targetPath }); - } catch (mkdirError) { - if (mkdirError.code === 'EEXIST') { - return res.status(409).json({ error: 'Folder already exists' }); - } - throw mkdirError; - } - } catch (error) { - console.error('Error creating folder:', error); - res.status(500).json({ error: 'Failed to create folder' }); - } -}); - -// Read file content endpoint -app.get('/api/projects/:projectName/file', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { filePath } = req.query; - - - // Security: ensure the requested path is inside the project root - if (!filePath) { - return res.status(400).json({ error: 'Invalid file path' }); - } - - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - // Handle both absolute and relative paths - const resolved = path.isAbsolute(filePath) - ? path.resolve(filePath) - : path.resolve(projectRoot, filePath); - const normalizedRoot = path.resolve(projectRoot) + path.sep; - if (!resolved.startsWith(normalizedRoot)) { - return res.status(403).json({ error: 'Path must be under project root' }); - } - - const content = await fsPromises.readFile(resolved, 'utf8'); - res.json({ content, path: resolved }); - } catch (error) { - console.error('Error reading file:', error); - if (error.code === 'ENOENT') { - res.status(404).json({ error: 'File not found' }); - } else if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else { - res.status(500).json({ error: error.message }); - } - } -}); - -// Serve binary file content endpoint (for images, etc.) -app.get('/api/projects/:projectName/files/content', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { path: filePath } = req.query; - - - // Security: ensure the requested path is inside the project root - if (!filePath) { - return res.status(400).json({ error: 'Invalid file path' }); - } - - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - const resolved = path.resolve(filePath); - const normalizedRoot = path.resolve(projectRoot) + path.sep; - if (!resolved.startsWith(normalizedRoot)) { - return res.status(403).json({ error: 'Path must be under project root' }); - } - - // Check if file exists - try { - await fsPromises.access(resolved); - } catch (error) { - return res.status(404).json({ error: 'File not found' }); - } - - // Get file extension and set appropriate content type - const mimeType = mime.lookup(resolved) || 'application/octet-stream'; - res.setHeader('Content-Type', mimeType); - - // Stream the file - const fileStream = fs.createReadStream(resolved); - fileStream.pipe(res); - - fileStream.on('error', (error) => { - console.error('Error streaming file:', error); - if (!res.headersSent) { - res.status(500).json({ error: 'Error reading file' }); - } - }); - - } catch (error) { - console.error('Error serving binary file:', error); - if (!res.headersSent) { - res.status(500).json({ error: error.message }); - } - } -}); - -// Save file content endpoint -app.put('/api/projects/:projectName/file', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { filePath, content } = req.body; - - - // Security: ensure the requested path is inside the project root - if (!filePath) { - return res.status(400).json({ error: 'Invalid file path' }); - } - - if (content === undefined) { - return res.status(400).json({ error: 'Content is required' }); - } - - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - // Handle both absolute and relative paths - const resolved = path.isAbsolute(filePath) - ? path.resolve(filePath) - : path.resolve(projectRoot, filePath); - const normalizedRoot = path.resolve(projectRoot) + path.sep; - if (!resolved.startsWith(normalizedRoot)) { - return res.status(403).json({ error: 'Path must be under project root' }); - } - - // Write the new content - await fsPromises.writeFile(resolved, content, 'utf8'); - - res.json({ - success: true, - path: resolved, - message: 'File saved successfully' - }); - } catch (error) { - console.error('Error saving file:', error); - if (error.code === 'ENOENT') { - res.status(404).json({ error: 'File or directory not found' }); - } else if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else { - res.status(500).json({ error: error.message }); - } - } -}); - -app.get('/api/projects/:projectName/files', authenticateToken, async (req, res) => { - try { - - // Using fsPromises from import - - // Use extractProjectDirectory to get the actual project path - let actualPath; - try { - actualPath = await extractProjectDirectory(req.params.projectName); - } catch (error) { - console.error('Error extracting project directory:', error); - // Fallback to simple dash replacement - actualPath = req.params.projectName.replace(/-/g, '/'); - } - - // Check if path exists - try { - await fsPromises.access(actualPath); - } catch (e) { - return res.status(404).json({ error: `Project path not found: ${actualPath}` }); - } - - const files = await getFileTree(actualPath, 10, 0, true); - const hiddenFiles = files.filter(f => f.name.startsWith('.')); - res.json(files); - } catch (error) { - console.error('[ERROR] File tree error:', error.message); - res.status(500).json({ error: error.message }); - } -}); - -// ============================================================================ -// FILE OPERATIONS API ENDPOINTS -// ============================================================================ - -/** - * Validate that a path is within the project root - * @param {string} projectRoot - The project root path - * @param {string} targetPath - The path to validate - * @returns {{ valid: boolean, resolved?: string, error?: string }} - */ -function validatePathInProject(projectRoot, targetPath) { - const resolved = path.isAbsolute(targetPath) - ? path.resolve(targetPath) - : path.resolve(projectRoot, targetPath); - const normalizedRoot = path.resolve(projectRoot) + path.sep; - if (!resolved.startsWith(normalizedRoot)) { - return { valid: false, error: 'Path must be under project root' }; - } - return { valid: true, resolved }; -} - -/** - * Validate filename - check for invalid characters - * @param {string} name - The filename to validate - * @returns {{ valid: boolean, error?: string }} - */ -function validateFilename(name) { - if (!name || !name.trim()) { - return { valid: false, error: 'Filename cannot be empty' }; - } - // Check for invalid characters (Windows + Unix) - const invalidChars = /[<>:"/\\|?*\x00-\x1f]/; - if (invalidChars.test(name)) { - return { valid: false, error: 'Filename contains invalid characters' }; - } - // Check for reserved names (Windows) - const reserved = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])$/i; - if (reserved.test(name)) { - return { valid: false, error: 'Filename is a reserved name' }; - } - // Check for dots only - if (/^\.+$/.test(name)) { - return { valid: false, error: 'Filename cannot be only dots' }; - } - return { valid: true }; -} - -// POST /api/projects/:projectName/files/create - Create new file or directory -app.post('/api/projects/:projectName/files/create', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { path: parentPath, type, name } = req.body; - - // Validate input - if (!name || !type) { - return res.status(400).json({ error: 'Name and type are required' }); - } - - if (!['file', 'directory'].includes(type)) { - return res.status(400).json({ error: 'Type must be "file" or "directory"' }); - } - - const nameValidation = validateFilename(name); - if (!nameValidation.valid) { - return res.status(400).json({ error: nameValidation.error }); - } - - // Get project root - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - // Build and validate target path - const targetDir = parentPath || ''; - const targetPath = targetDir ? path.join(targetDir, name) : name; - const validation = validatePathInProject(projectRoot, targetPath); - if (!validation.valid) { - return res.status(403).json({ error: validation.error }); - } - - const resolvedPath = validation.resolved; - - // Check if already exists - try { - await fsPromises.access(resolvedPath); - return res.status(409).json({ error: `${type === 'file' ? 'File' : 'Directory'} already exists` }); - } catch { - // Doesn't exist, which is what we want - } - - // Create file or directory - if (type === 'directory') { - await fsPromises.mkdir(resolvedPath, { recursive: false }); - } else { - // Ensure parent directory exists - const parentDir = path.dirname(resolvedPath); - try { - await fsPromises.access(parentDir); - } catch { - await fsPromises.mkdir(parentDir, { recursive: true }); - } - await fsPromises.writeFile(resolvedPath, '', 'utf8'); - } - - res.json({ - success: true, - path: resolvedPath, - name, - type, - message: `${type === 'file' ? 'File' : 'Directory'} created successfully` - }); - } catch (error) { - console.error('Error creating file/directory:', error); - if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else if (error.code === 'ENOENT') { - res.status(404).json({ error: 'Parent directory not found' }); - } else { - res.status(500).json({ error: error.message }); - } - } -}); - -// PUT /api/projects/:projectName/files/rename - Rename file or directory -app.put('/api/projects/:projectName/files/rename', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { oldPath, newName } = req.body; - - // Validate input - if (!oldPath || !newName) { - return res.status(400).json({ error: 'oldPath and newName are required' }); - } - - const nameValidation = validateFilename(newName); - if (!nameValidation.valid) { - return res.status(400).json({ error: nameValidation.error }); - } - - // Get project root - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - // Validate old path - const oldValidation = validatePathInProject(projectRoot, oldPath); - if (!oldValidation.valid) { - return res.status(403).json({ error: oldValidation.error }); - } - - const resolvedOldPath = oldValidation.resolved; - - // Check if old path exists - try { - await fsPromises.access(resolvedOldPath); - } catch { - return res.status(404).json({ error: 'File or directory not found' }); - } - - // Build and validate new path - const parentDir = path.dirname(resolvedOldPath); - const resolvedNewPath = path.join(parentDir, newName); - const newValidation = validatePathInProject(projectRoot, resolvedNewPath); - if (!newValidation.valid) { - return res.status(403).json({ error: newValidation.error }); - } - - // Check if new path already exists - try { - await fsPromises.access(resolvedNewPath); - return res.status(409).json({ error: 'A file or directory with this name already exists' }); - } catch { - // Doesn't exist, which is what we want - } - - // Rename - await fsPromises.rename(resolvedOldPath, resolvedNewPath); - - res.json({ - success: true, - oldPath: resolvedOldPath, - newPath: resolvedNewPath, - newName, - message: 'Renamed successfully' - }); - } catch (error) { - console.error('Error renaming file/directory:', error); - if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else if (error.code === 'ENOENT') { - res.status(404).json({ error: 'File or directory not found' }); - } else if (error.code === 'EXDEV') { - res.status(400).json({ error: 'Cannot move across different filesystems' }); - } else { - res.status(500).json({ error: error.message }); - } - } -}); - -// DELETE /api/projects/:projectName/files - Delete file or directory -app.delete('/api/projects/:projectName/files', authenticateToken, async (req, res) => { - try { - const { projectName } = req.params; - const { path: targetPath, type } = req.body; - - // Validate input - if (!targetPath) { - return res.status(400).json({ error: 'Path is required' }); - } - - // Get project root - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - // Validate path - const validation = validatePathInProject(projectRoot, targetPath); - if (!validation.valid) { - return res.status(403).json({ error: validation.error }); - } - - const resolvedPath = validation.resolved; - - // Check if path exists and get stats - let stats; - try { - stats = await fsPromises.stat(resolvedPath); - } catch { - return res.status(404).json({ error: 'File or directory not found' }); - } - - // Prevent deleting the project root itself - if (resolvedPath === path.resolve(projectRoot)) { - return res.status(403).json({ error: 'Cannot delete project root directory' }); - } - - // Delete based on type - if (stats.isDirectory()) { - await fsPromises.rm(resolvedPath, { recursive: true, force: true }); - } else { - await fsPromises.unlink(resolvedPath); - } - - res.json({ - success: true, - path: resolvedPath, - type: stats.isDirectory() ? 'directory' : 'file', - message: 'Deleted successfully' - }); - } catch (error) { - console.error('Error deleting file/directory:', error); - if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else if (error.code === 'ENOENT') { - res.status(404).json({ error: 'File or directory not found' }); - } else if (error.code === 'ENOTEMPTY') { - res.status(400).json({ error: 'Directory is not empty' }); - } else { - res.status(500).json({ error: error.message }); - } - } -}); - -// POST /api/projects/:projectName/files/upload - Upload files -// Dynamic import of multer for file uploads -const uploadFilesHandler = async (req, res) => { - // Dynamic import of multer - const multer = (await import('multer')).default; - - const uploadMiddleware = multer({ - storage: multer.diskStorage({ - destination: (req, file, cb) => { - cb(null, os.tmpdir()); - }, - filename: (req, file, cb) => { - // Use a unique temp name, but preserve original name in file.originalname - // Note: file.originalname may contain path separators for folder uploads - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); - // For temp file, just use a safe unique name without the path - cb(null, `upload-${uniqueSuffix}`); - } - }), - limits: { - fileSize: 50 * 1024 * 1024, // 50MB limit - files: 20 // Max 20 files at once - } - }); - - // Use multer middleware - uploadMiddleware.array('files', 20)(req, res, async (err) => { - if (err) { - console.error('Multer error:', err); - if (err.code === 'LIMIT_FILE_SIZE') { - return res.status(400).json({ error: 'File too large. Maximum size is 50MB.' }); - } - if (err.code === 'LIMIT_FILE_COUNT') { - return res.status(400).json({ error: 'Too many files. Maximum is 20 files.' }); - } - return res.status(500).json({ error: err.message }); - } - - try { - const { projectName } = req.params; - const { targetPath, relativePaths } = req.body; - - // Parse relative paths if provided (for folder uploads) - let filePaths = []; - if (relativePaths) { - try { - filePaths = JSON.parse(relativePaths); - } catch (e) { - console.log('[DEBUG] Failed to parse relativePaths:', relativePaths); - } - } - - console.log('[DEBUG] File upload request:', { - projectName, - targetPath: JSON.stringify(targetPath), - targetPathType: typeof targetPath, - filesCount: req.files?.length, - relativePaths: filePaths - }); - - if (!req.files || req.files.length === 0) { - return res.status(400).json({ error: 'No files provided' }); - } - - // Get project root - const projectRoot = await extractProjectDirectory(projectName).catch(() => null); - if (!projectRoot) { - return res.status(404).json({ error: 'Project not found' }); - } - - console.log('[DEBUG] Project root:', projectRoot); - - // Validate and resolve target path - // If targetPath is empty or '.', use project root directly - const targetDir = targetPath || ''; - let resolvedTargetDir; - - console.log('[DEBUG] Target dir:', JSON.stringify(targetDir)); - - if (!targetDir || targetDir === '.' || targetDir === './') { - // Empty path means upload to project root - resolvedTargetDir = path.resolve(projectRoot); - console.log('[DEBUG] Using project root as target:', resolvedTargetDir); - } else { - const validation = validatePathInProject(projectRoot, targetDir); - if (!validation.valid) { - console.log('[DEBUG] Path validation failed:', validation.error); - return res.status(403).json({ error: validation.error }); - } - resolvedTargetDir = validation.resolved; - console.log('[DEBUG] Resolved target dir:', resolvedTargetDir); - } - - // Ensure target directory exists - try { - await fsPromises.access(resolvedTargetDir); - } catch { - await fsPromises.mkdir(resolvedTargetDir, { recursive: true }); - } - - // Move uploaded files from temp to target directory - const uploadedFiles = []; - console.log('[DEBUG] Processing files:', req.files.map(f => ({ originalname: f.originalname, path: f.path }))); - for (let i = 0; i < req.files.length; i++) { - const file = req.files[i]; - // Use relative path if provided (for folder uploads), otherwise use originalname - const fileName = (filePaths && filePaths[i]) ? filePaths[i] : file.originalname; - console.log('[DEBUG] Processing file:', fileName, '(originalname:', file.originalname + ')'); - const destPath = path.join(resolvedTargetDir, fileName); - - // Validate destination path - const destValidation = validatePathInProject(projectRoot, destPath); - if (!destValidation.valid) { - console.log('[DEBUG] Destination validation failed for:', destPath); - // Clean up temp file - await fsPromises.unlink(file.path).catch(() => {}); - continue; - } - - // Ensure parent directory exists (for nested files from folder upload) - const parentDir = path.dirname(destPath); - try { - await fsPromises.access(parentDir); - } catch { - await fsPromises.mkdir(parentDir, { recursive: true }); - } - - // Move file (copy + unlink to handle cross-device scenarios) - await fsPromises.copyFile(file.path, destPath); - await fsPromises.unlink(file.path); - - uploadedFiles.push({ - name: fileName, - path: destPath, - size: file.size, - mimeType: file.mimetype - }); - } - - res.json({ - success: true, - files: uploadedFiles, - targetPath: resolvedTargetDir, - message: `Uploaded ${uploadedFiles.length} file(s) successfully` - }); - } catch (error) { - console.error('Error uploading files:', error); - // Clean up any remaining temp files - if (req.files) { - for (const file of req.files) { - await fsPromises.unlink(file.path).catch(() => {}); - } - } - if (error.code === 'EACCES') { - res.status(403).json({ error: 'Permission denied' }); - } else { - res.status(500).json({ error: error.message }); - } - } - }); -}; - -app.post('/api/projects/:projectName/files/upload', authenticateToken, uploadFilesHandler); - -// WebSocket connection handler that routes based on URL path -wss.on('connection', (ws, request) => { - const url = request.url; - console.log('[INFO] Client connected to:', url); - - // Parse URL to get pathname without query parameters - const urlObj = new URL(url, 'http://localhost'); - const pathname = urlObj.pathname; - - if (pathname === '/shell') { - handleShellConnection(ws); - } else if (pathname === '/ws') { - handleChatConnection(ws); - } else { - console.log('[WARN] Unknown WebSocket path:', pathname); - ws.close(); - } -}); - -/** - * WebSocket Writer - Wrapper for WebSocket to match SSEStreamWriter interface - */ -class WebSocketWriter { - constructor(ws) { - this.ws = ws; - this.sessionId = null; - this.isWebSocketWriter = true; // Marker for transport detection - } - - send(data) { - if (this.ws.readyState === 1) { // WebSocket.OPEN - // Providers send raw objects, we stringify for WebSocket - this.ws.send(JSON.stringify(data)); - } - } - - updateWebSocket(newRawWs) { - this.ws = newRawWs; - } - - setSessionId(sessionId) { - this.sessionId = sessionId; - } - - getSessionId() { - return this.sessionId; - } -} - -// Handle chat WebSocket connections -function handleChatConnection(ws) { - console.log('[INFO] Chat WebSocket connected'); - - // Add to connected clients for project updates - connectedClients.add(ws); - - // Wrap WebSocket with writer for consistent interface with SSEStreamWriter - const writer = new WebSocketWriter(ws); - - ws.on('message', async (message) => { - try { - const data = JSON.parse(message); - - if (data.type === 'claude-command') { - console.log('[DEBUG] User message:', data.command || '[Continue/Resume]'); - console.log('๐Ÿ“ Project:', data.options?.projectPath || 'Unknown'); - console.log('๐Ÿ”„ Session:', data.options?.sessionId ? 'Resume' : 'New'); - - // Use Claude Agents SDK - await queryClaudeSDK(data.command, data.options, writer); - } else if (data.type === 'cursor-command') { - console.log('[DEBUG] Cursor message:', data.command || '[Continue/Resume]'); - console.log('๐Ÿ“ Project:', data.options?.cwd || 'Unknown'); - console.log('๐Ÿ”„ Session:', data.options?.sessionId ? 'Resume' : 'New'); - console.log('๐Ÿค– Model:', data.options?.model || 'default'); - await spawnCursor(data.command, data.options, writer); - } else if (data.type === 'codex-command') { - console.log('[DEBUG] Codex message:', data.command || '[Continue/Resume]'); - console.log('๐Ÿ“ Project:', data.options?.projectPath || data.options?.cwd || 'Unknown'); - console.log('๐Ÿ”„ Session:', data.options?.sessionId ? 'Resume' : 'New'); - console.log('๐Ÿค– Model:', data.options?.model || 'default'); - await queryCodex(data.command, data.options, writer); - } else if (data.type === 'gemini-command') { - console.log('[DEBUG] Gemini message:', data.command || '[Continue/Resume]'); - console.log('๐Ÿ“ Project:', data.options?.projectPath || data.options?.cwd || 'Unknown'); - console.log('๐Ÿ”„ Session:', data.options?.sessionId ? 'Resume' : 'New'); - console.log('๐Ÿค– Model:', data.options?.model || 'default'); - await spawnGemini(data.command, data.options, writer); - } else if (data.type === 'cursor-resume') { - // Backward compatibility: treat as cursor-command with resume and no prompt - console.log('[DEBUG] Cursor resume session (compat):', data.sessionId); - await spawnCursor('', { - sessionId: data.sessionId, - resume: true, - cwd: data.options?.cwd - }, writer); - } else if (data.type === 'abort-session') { - console.log('[DEBUG] Abort session request:', data.sessionId); - const provider = data.provider || 'claude'; - let success; - - if (provider === 'cursor') { - success = abortCursorSession(data.sessionId); - } else if (provider === 'codex') { - success = abortCodexSession(data.sessionId); - } else if (provider === 'gemini') { - success = abortGeminiSession(data.sessionId); - } else { - // Use Claude Agents SDK - success = await abortClaudeSDKSession(data.sessionId); - } - - writer.send({ - type: 'session-aborted', - sessionId: data.sessionId, - provider, - success - }); - } else if (data.type === 'claude-permission-response') { - // Relay UI approval decisions back into the SDK control flow. - // This does not persist permissions; it only resolves the in-flight request, - // introduced so the SDK can resume once the user clicks Allow/Deny. - if (data.requestId) { - resolveToolApproval(data.requestId, { - allow: Boolean(data.allow), - updatedInput: data.updatedInput, - message: data.message, - rememberEntry: data.rememberEntry - }); - } - } else if (data.type === 'cursor-abort') { - console.log('[DEBUG] Abort Cursor session:', data.sessionId); - const success = abortCursorSession(data.sessionId); - writer.send({ - type: 'session-aborted', - sessionId: data.sessionId, - provider: 'cursor', - success - }); - } else if (data.type === 'check-session-status') { - // Check if a specific session is currently processing - const provider = data.provider || 'claude'; - const sessionId = data.sessionId; - let isActive; - - if (provider === 'cursor') { - isActive = isCursorSessionActive(sessionId); - } else if (provider === 'codex') { - isActive = isCodexSessionActive(sessionId); - } else if (provider === 'gemini') { - isActive = isGeminiSessionActive(sessionId); - } else { - // Use Claude Agents SDK - isActive = isClaudeSDKSessionActive(sessionId); - if (isActive) { - // Reconnect the session's writer to the new WebSocket so - // subsequent SDK output flows to the refreshed client. - reconnectSessionWriter(sessionId, ws); - } - } - - writer.send({ - type: 'session-status', - sessionId, - provider, - isProcessing: isActive - }); - } else if (data.type === 'get-pending-permissions') { - // Return pending permission requests for a session - const sessionId = data.sessionId; - if (sessionId && isClaudeSDKSessionActive(sessionId)) { - const pending = getPendingApprovalsForSession(sessionId); - writer.send({ - type: 'pending-permissions-response', - sessionId, - data: pending - }); - } - } else if (data.type === 'get-active-sessions') { - // Get all currently active sessions - const activeSessions = { - claude: getActiveClaudeSDKSessions(), - cursor: getActiveCursorSessions(), - codex: getActiveCodexSessions(), - gemini: getActiveGeminiSessions() - }; - writer.send({ - type: 'active-sessions', - sessions: activeSessions - }); - } - } catch (error) { - console.error('[ERROR] Chat WebSocket error:', error.message); - writer.send({ - type: 'error', - error: error.message - }); - } - }); - - ws.on('close', () => { - console.log('๐Ÿ”Œ Chat client disconnected'); - // Remove from connected clients - connectedClients.delete(ws); - }); -} - -// Handle shell WebSocket connections -function handleShellConnection(ws) { - console.log('๐Ÿš Shell client connected'); - let shellProcess = null; - let ptySessionKey = null; - let urlDetectionBuffer = ''; - const announcedAuthUrls = new Set(); - - ws.on('message', async (message) => { - try { - const data = JSON.parse(message); - console.log('๐Ÿ“จ Shell message received:', data.type); - - if (data.type === 'init') { - const projectPath = data.projectPath || process.cwd(); - const sessionId = data.sessionId; - const hasSession = data.hasSession; - const provider = data.provider || 'claude'; - const initialCommand = data.initialCommand; - const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell'; - urlDetectionBuffer = ''; - announcedAuthUrls.clear(); - - // Login commands (Claude/Cursor auth) should never reuse cached sessions - const isLoginCommand = initialCommand && ( - initialCommand.includes('setup-token') || - initialCommand.includes('cursor-agent login') || - initialCommand.includes('auth login') - ); - - // Include command hash in session key so different commands get separate sessions - const commandSuffix = isPlainShell && initialCommand - ? `_cmd_${Buffer.from(initialCommand).toString('base64').slice(0, 16)}` - : ''; - ptySessionKey = `${projectPath}_${sessionId || 'default'}${commandSuffix}`; - - // Kill any existing login session before starting fresh - if (isLoginCommand) { - const oldSession = ptySessionsMap.get(ptySessionKey); - if (oldSession) { - console.log('๐Ÿงน Cleaning up existing login session:', ptySessionKey); - if (oldSession.timeoutId) clearTimeout(oldSession.timeoutId); - if (oldSession.pty && oldSession.pty.kill) oldSession.pty.kill(); - ptySessionsMap.delete(ptySessionKey); - } - } - - const existingSession = isLoginCommand ? null : ptySessionsMap.get(ptySessionKey); - if (existingSession) { - console.log('โ™ป๏ธ Reconnecting to existing PTY session:', ptySessionKey); - shellProcess = existingSession.pty; - - clearTimeout(existingSession.timeoutId); - - ws.send(JSON.stringify({ - type: 'output', - data: `\x1b[36m[Reconnected to existing session]\x1b[0m\r\n` - })); - - if (existingSession.buffer && existingSession.buffer.length > 0) { - console.log(`๐Ÿ“œ Sending ${existingSession.buffer.length} buffered messages`); - existingSession.buffer.forEach(bufferedData => { - ws.send(JSON.stringify({ - type: 'output', - data: bufferedData - })); - }); - } - - existingSession.ws = ws; - - return; - } - - console.log('[INFO] Starting shell in:', projectPath); - console.log('๐Ÿ“‹ Session info:', hasSession ? `Resume session ${sessionId}` : (isPlainShell ? 'Plain shell mode' : 'New session')); - console.log('๐Ÿค– Provider:', isPlainShell ? 'plain-shell' : provider); - if (initialCommand) { - console.log('โšก Initial command:', initialCommand); - } - - // First send a welcome message - let welcomeMsg; - if (isPlainShell) { - welcomeMsg = `\x1b[36mStarting terminal in: ${projectPath}\x1b[0m\r\n`; - } else { - const providerName = provider === 'cursor' ? 'Cursor' : (provider === 'codex' ? 'Codex' : (provider === 'gemini' ? 'Gemini' : 'Claude')); - welcomeMsg = hasSession ? - `\x1b[36mResuming ${providerName} session ${sessionId} in: ${projectPath}\x1b[0m\r\n` : - `\x1b[36mStarting new ${providerName} session in: ${projectPath}\x1b[0m\r\n`; - } - - ws.send(JSON.stringify({ - type: 'output', - data: welcomeMsg - })); - - try { - // Validate projectPath โ€” resolve to absolute and verify it exists - const resolvedProjectPath = path.resolve(projectPath); - try { - const stats = fs.statSync(resolvedProjectPath); - if (!stats.isDirectory()) { - throw new Error('Not a directory'); - } - } catch (pathErr) { - ws.send(JSON.stringify({ type: 'error', message: 'Invalid project path' })); - return; - } - - // Validate sessionId โ€” only allow safe characters - const safeSessionIdPattern = /^[a-zA-Z0-9_.\-:]+$/; - if (sessionId && !safeSessionIdPattern.test(sessionId)) { - ws.send(JSON.stringify({ type: 'error', message: 'Invalid session ID' })); - return; - } - - // Build shell command โ€” use cwd for project path (never interpolate into shell string) - let shellCommand; - if (isPlainShell) { - // Plain shell mode - run the initial command in the project directory - shellCommand = initialCommand; - } else if (provider === 'cursor') { - if (hasSession && sessionId) { - shellCommand = `cursor-agent --resume="${sessionId}"`; - } else { - shellCommand = 'cursor-agent'; - } - } else if (provider === 'codex') { - // Use codex command; attempt to resume and fall back to a new session when the resume fails. - if (hasSession && sessionId) { - if (os.platform() === 'win32') { - // PowerShell syntax for fallback - shellCommand = `codex resume "${sessionId}"; if ($LASTEXITCODE -ne 0) { codex }`; - } else { - shellCommand = `codex resume "${sessionId}" || codex`; - } - } else { - shellCommand = 'codex'; - } - } else if (provider === 'gemini') { - const command = initialCommand || 'gemini'; - let resumeId = sessionId; - if (hasSession && sessionId) { - try { - // Gemini CLI enforces its own native session IDs, unlike other agents that accept arbitrary string names. - // The UI only knows about its internal generated `sessionId` (e.g. gemini_1234). - // We must fetch the mapping from the backend session manager to pass the native `cliSessionId` to the shell. - const sess = sessionManager.getSession(sessionId); - if (sess && sess.cliSessionId) { - resumeId = sess.cliSessionId; - // Validate the looked-up CLI session ID too - if (!safeSessionIdPattern.test(resumeId)) { - resumeId = null; - } - } - } catch (err) { - console.error('Failed to get Gemini CLI session ID:', err); - } - } - - if (hasSession && resumeId) { - shellCommand = `${command} --resume "${resumeId}"`; - } else { - shellCommand = command; - } - } else { - // Claude (default provider) - const command = initialCommand || 'claude'; - if (hasSession && sessionId) { - if (os.platform() === 'win32') { - shellCommand = `claude --resume "${sessionId}"; if ($LASTEXITCODE -ne 0) { claude }`; - } else { - shellCommand = `claude --resume "${sessionId}" || claude`; - } - } else { - shellCommand = command; - } - } - - console.log('๐Ÿ”ง Executing shell command:', shellCommand); - - // Use appropriate shell based on platform - const shell = os.platform() === 'win32' ? 'powershell.exe' : 'bash'; - const shellArgs = os.platform() === 'win32' ? ['-Command', shellCommand] : ['-c', shellCommand]; - - // Use terminal dimensions from client if provided, otherwise use defaults - const termCols = data.cols || 80; - const termRows = data.rows || 24; - console.log('๐Ÿ“ Using terminal dimensions:', termCols, 'x', termRows); - - shellProcess = pty.spawn(shell, shellArgs, { - name: 'xterm-256color', - cols: termCols, - rows: termRows, - cwd: resolvedProjectPath, - env: { - ...process.env, - TERM: 'xterm-256color', - COLORTERM: 'truecolor', - FORCE_COLOR: '3' - } - }); - - console.log('๐ŸŸข Shell process started with PTY, PID:', shellProcess.pid); - - ptySessionsMap.set(ptySessionKey, { - pty: shellProcess, - ws: ws, - buffer: [], - timeoutId: null, - projectPath, - sessionId - }); - - // Handle data output - shellProcess.onData((data) => { - const session = ptySessionsMap.get(ptySessionKey); - if (!session) return; - - if (session.buffer.length < 5000) { - session.buffer.push(data); - } else { - session.buffer.shift(); - session.buffer.push(data); - } - - if (session.ws && session.ws.readyState === WebSocket.OPEN) { - let outputData = data; - - const cleanChunk = stripAnsiSequences(data); - urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT); - - outputData = outputData.replace( - /OPEN_URL:\s*(https?:\/\/[^\s\x1b\x07]+)/g, - '[INFO] Opening in browser: $1' - ); - - const emitAuthUrl = (detectedUrl, autoOpen = false) => { - const normalizedUrl = normalizeDetectedUrl(detectedUrl); - if (!normalizedUrl) return; - - const isNewUrl = !announcedAuthUrls.has(normalizedUrl); - if (isNewUrl) { - announcedAuthUrls.add(normalizedUrl); - session.ws.send(JSON.stringify({ - type: 'auth_url', - url: normalizedUrl, - autoOpen - })); - } - - }; - - const normalizedDetectedUrls = extractUrlsFromText(urlDetectionBuffer) - .map((url) => normalizeDetectedUrl(url)) - .filter(Boolean); - - // Prefer the most complete URL if shorter prefix variants are also present. - const dedupedDetectedUrls = Array.from(new Set(normalizedDetectedUrls)).filter((url, _, urls) => - !urls.some((otherUrl) => otherUrl !== url && otherUrl.startsWith(url)) - ); - - dedupedDetectedUrls.forEach((url) => emitAuthUrl(url, false)); - - if (shouldAutoOpenUrlFromOutput(cleanChunk) && dedupedDetectedUrls.length > 0) { - const bestUrl = dedupedDetectedUrls.reduce((longest, current) => - current.length > longest.length ? current : longest - ); - emitAuthUrl(bestUrl, true); - } - - // Send regular output - session.ws.send(JSON.stringify({ - type: 'output', - data: outputData - })); - } - }); - - // Handle process exit - shellProcess.onExit((exitCode) => { - console.log('๐Ÿ”š Shell process exited with code:', exitCode.exitCode, 'signal:', exitCode.signal); - const session = ptySessionsMap.get(ptySessionKey); - if (session && session.ws && session.ws.readyState === WebSocket.OPEN) { - session.ws.send(JSON.stringify({ - type: 'output', - data: `\r\n\x1b[33mProcess exited with code ${exitCode.exitCode}${exitCode.signal ? ` (${exitCode.signal})` : ''}\x1b[0m\r\n` - })); - } - if (session && session.timeoutId) { - clearTimeout(session.timeoutId); - } - ptySessionsMap.delete(ptySessionKey); - shellProcess = null; - }); - - } catch (spawnError) { - console.error('[ERROR] Error spawning process:', spawnError); - ws.send(JSON.stringify({ - type: 'output', - data: `\r\n\x1b[31mError: ${spawnError.message}\x1b[0m\r\n` - })); - } - - } else if (data.type === 'input') { - // Send input to shell process - if (shellProcess && shellProcess.write) { - try { - shellProcess.write(data.data); - } catch (error) { - console.error('Error writing to shell:', error); - } - } else { - console.warn('No active shell process to send input to'); - } - } else if (data.type === 'resize') { - // Handle terminal resize - if (shellProcess && shellProcess.resize) { - console.log('Terminal resize requested:', data.cols, 'x', data.rows); - shellProcess.resize(data.cols, data.rows); - } - } - } catch (error) { - console.error('[ERROR] Shell WebSocket error:', error.message); - if (ws.readyState === WebSocket.OPEN) { - ws.send(JSON.stringify({ - type: 'output', - data: `\r\n\x1b[31mError: ${error.message}\x1b[0m\r\n` - })); - } - } - }); - - ws.on('close', () => { - console.log('๐Ÿ”Œ Shell client disconnected'); - - if (ptySessionKey) { - const session = ptySessionsMap.get(ptySessionKey); - if (session) { - console.log('โณ PTY session kept alive, will timeout in 30 minutes:', ptySessionKey); - session.ws = null; - - session.timeoutId = setTimeout(() => { - console.log('โฐ PTY session timeout, killing process:', ptySessionKey); - if (session.pty && session.pty.kill) { - session.pty.kill(); - } - ptySessionsMap.delete(ptySessionKey); - }, PTY_SESSION_TIMEOUT); - } - } - }); - - ws.on('error', (error) => { - console.error('[ERROR] Shell WebSocket error:', error); - }); -} -// Audio transcription endpoint -app.post('/api/transcribe', authenticateToken, async (req, res) => { - try { - const multer = (await import('multer')).default; - const upload = multer({ storage: multer.memoryStorage() }); - - // Handle multipart form data - upload.single('audio')(req, res, async (err) => { - if (err) { - return res.status(400).json({ error: 'Failed to process audio file' }); - } - - if (!req.file) { - return res.status(400).json({ error: 'No audio file provided' }); - } - - const apiKey = process.env.OPENAI_API_KEY; - if (!apiKey) { - return res.status(500).json({ error: 'OpenAI API key not configured. Please set OPENAI_API_KEY in server environment.' }); - } - - try { - // Create form data for OpenAI - const FormData = (await import('form-data')).default; - const formData = new FormData(); - formData.append('file', req.file.buffer, { - filename: req.file.originalname, - contentType: req.file.mimetype - }); - formData.append('model', 'whisper-1'); - formData.append('response_format', 'json'); - formData.append('language', 'en'); - - // Make request to OpenAI - const response = await fetch('https://api.openai.com/v1/audio/transcriptions', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${apiKey}`, - ...formData.getHeaders() - }, - body: formData - }); - - if (!response.ok) { - const errorData = await response.json().catch(() => ({})); - throw new Error(errorData.error?.message || `Whisper API error: ${response.status}`); - } - - const data = await response.json(); - let transcribedText = data.text || ''; - - // Check if enhancement mode is enabled - const mode = req.body.mode || 'default'; - - // If no transcribed text, return empty - if (!transcribedText) { - return res.json({ text: '' }); - } - - // If default mode, return transcribed text without enhancement - if (mode === 'default') { - return res.json({ text: transcribedText }); - } - - // Handle different enhancement modes - try { - const OpenAI = (await import('openai')).default; - const openai = new OpenAI({ apiKey }); - - let prompt, systemMessage, temperature = 0.7, maxTokens = 800; - - switch (mode) { - case 'prompt': - systemMessage = 'You are an expert prompt engineer who creates clear, detailed, and effective prompts.'; - prompt = `You are an expert prompt engineer. Transform the following rough instruction into a clear, detailed, and context-aware AI prompt. - -Your enhanced prompt should: -1. Be specific and unambiguous -2. Include relevant context and constraints -3. Specify the desired output format -4. Use clear, actionable language -5. Include examples where helpful -6. Consider edge cases and potential ambiguities - -Transform this rough instruction into a well-crafted prompt: -"${transcribedText}" - -Enhanced prompt:`; - break; - - case 'vibe': - case 'instructions': - case 'architect': - systemMessage = 'You are a helpful assistant that formats ideas into clear, actionable instructions for AI agents.'; - temperature = 0.5; // Lower temperature for more controlled output - prompt = `Transform the following idea into clear, well-structured instructions that an AI agent can easily understand and execute. - -IMPORTANT RULES: -- Format as clear, step-by-step instructions -- Add reasonable implementation details based on common patterns -- Only include details directly related to what was asked -- Do NOT add features or functionality not mentioned -- Keep the original intent and scope intact -- Use clear, actionable language an agent can follow - -Transform this idea into agent-friendly instructions: -"${transcribedText}" - -Agent instructions:`; - break; - - default: - // No enhancement needed - break; - } - - // Only make GPT call if we have a prompt - if (prompt) { - const completion = await openai.chat.completions.create({ - model: 'gpt-4o-mini', - messages: [ - { role: 'system', content: systemMessage }, - { role: 'user', content: prompt } - ], - temperature: temperature, - max_tokens: maxTokens - }); - - transcribedText = completion.choices[0].message.content || transcribedText; - } - - } catch (gptError) { - console.error('GPT processing error:', gptError); - // Fall back to original transcription if GPT fails - } - - res.json({ text: transcribedText }); - - } catch (error) { - console.error('Transcription error:', error); - res.status(500).json({ error: error.message }); - } - }); - } catch (error) { - console.error('Endpoint error:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Image upload endpoint -app.post('/api/projects/:projectName/upload-images', authenticateToken, async (req, res) => { - try { - const multer = (await import('multer')).default; - const path = (await import('path')).default; - const fs = (await import('fs')).promises; - const os = (await import('os')).default; - - // Configure multer for image uploads - const storage = multer.diskStorage({ - destination: async (req, file, cb) => { - const uploadDir = path.join(os.tmpdir(), 'claude-ui-uploads', String(req.user.id)); - await fs.mkdir(uploadDir, { recursive: true }); - cb(null, uploadDir); - }, - filename: (req, file, cb) => { - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); - const sanitizedName = file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_'); - cb(null, uniqueSuffix + '-' + sanitizedName); - } - }); - - const fileFilter = (req, file, cb) => { - const allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml']; - if (allowedMimes.includes(file.mimetype)) { - cb(null, true); - } else { - cb(new Error('Invalid file type. Only JPEG, PNG, GIF, WebP, and SVG are allowed.')); - } - }; - - const upload = multer({ - storage, - fileFilter, - limits: { - fileSize: 5 * 1024 * 1024, // 5MB - files: 5 - } - }); - - // Handle multipart form data - upload.array('images', 5)(req, res, async (err) => { - if (err) { - return res.status(400).json({ error: err.message }); - } - - if (!req.files || req.files.length === 0) { - return res.status(400).json({ error: 'No image files provided' }); - } - - try { - // Process uploaded images - const processedImages = await Promise.all( - req.files.map(async (file) => { - // Read file and convert to base64 - const buffer = await fs.readFile(file.path); - const base64 = buffer.toString('base64'); - const mimeType = file.mimetype; - - // Clean up temp file immediately - await fs.unlink(file.path); - - return { - name: file.originalname, - data: `data:${mimeType};base64,${base64}`, - size: file.size, - mimeType: mimeType - }; - }) - ); - - res.json({ images: processedImages }); - } catch (error) { - console.error('Error processing images:', error); - // Clean up any remaining files - await Promise.all(req.files.map(f => fs.unlink(f.path).catch(() => { }))); - res.status(500).json({ error: 'Failed to process images' }); - } - }); - } catch (error) { - console.error('Error in image upload endpoint:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Get token usage for a specific session -app.get('/api/projects/:projectName/sessions/:sessionId/token-usage', authenticateToken, async (req, res) => { - try { - const { projectName, sessionId } = req.params; - const { provider = 'claude' } = req.query; - const homeDir = os.homedir(); - - // Allow only safe characters in sessionId - const safeSessionId = String(sessionId).replace(/[^a-zA-Z0-9._-]/g, ''); - if (!safeSessionId || safeSessionId !== String(sessionId)) { - return res.status(400).json({ error: 'Invalid sessionId' }); - } - - // Handle Cursor sessions - they use SQLite and don't have token usage info - if (provider === 'cursor') { - return res.json({ - used: 0, - total: 0, - breakdown: { input: 0, cacheCreation: 0, cacheRead: 0 }, - unsupported: true, - message: 'Token usage tracking not available for Cursor sessions' - }); - } - - // Handle Gemini sessions - they are raw logs in our current setup - if (provider === 'gemini') { - return res.json({ - used: 0, - total: 0, - breakdown: { input: 0, cacheCreation: 0, cacheRead: 0 }, - unsupported: true, - message: 'Token usage tracking not available for Gemini sessions' - }); - } - - // Handle Codex sessions - if (provider === 'codex') { - const codexSessionsDir = path.join(homeDir, '.codex', 'sessions'); - - // Find the session file by searching for the session ID - const findSessionFile = async (dir) => { - try { - const entries = await fsPromises.readdir(dir, { withFileTypes: true }); - for (const entry of entries) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - const found = await findSessionFile(fullPath); - if (found) return found; - } else if (entry.name.includes(safeSessionId) && entry.name.endsWith('.jsonl')) { - return fullPath; - } - } - } catch (error) { - // Skip directories we can't read - } - return null; - }; - - const sessionFilePath = await findSessionFile(codexSessionsDir); - - if (!sessionFilePath) { - return res.status(404).json({ error: 'Codex session file not found', sessionId: safeSessionId }); - } - - // Read and parse the Codex JSONL file - let fileContent; - try { - fileContent = await fsPromises.readFile(sessionFilePath, 'utf8'); - } catch (error) { - if (error.code === 'ENOENT') { - return res.status(404).json({ error: 'Session file not found', path: sessionFilePath }); - } - throw error; - } - const lines = fileContent.trim().split('\n'); - let totalTokens = 0; - let contextWindow = 200000; // Default for Codex/OpenAI - - // Find the latest token_count event with info (scan from end) - for (let i = lines.length - 1; i >= 0; i--) { - try { - const entry = JSON.parse(lines[i]); - - // Codex stores token info in event_msg with type: "token_count" - if (entry.type === 'event_msg' && entry.payload?.type === 'token_count' && entry.payload?.info) { - const tokenInfo = entry.payload.info; - if (tokenInfo.total_token_usage) { - totalTokens = tokenInfo.total_token_usage.total_tokens || 0; - } - if (tokenInfo.model_context_window) { - contextWindow = tokenInfo.model_context_window; - } - break; // Stop after finding the latest token count - } - } catch (parseError) { - // Skip lines that can't be parsed - continue; - } - } - - return res.json({ - used: totalTokens, - total: contextWindow - }); - } - - // Handle Claude sessions (default) - // Extract actual project path - let projectPath; - try { - projectPath = await extractProjectDirectory(projectName); - } catch (error) { - console.error('Error extracting project directory:', error); - return res.status(500).json({ error: 'Failed to determine project path' }); - } - - // Construct the JSONL file path - // Claude stores session files in ~/.claude/projects/[encoded-project-path]/[session-id].jsonl - // The encoding replaces any non-alphanumeric character (except -) with - - const encodedPath = projectPath.replace(/[^a-zA-Z0-9-]/g, '-'); - const projectDir = path.join(homeDir, '.claude', 'projects', encodedPath); - - const jsonlPath = path.join(projectDir, `${safeSessionId}.jsonl`); - - // Constrain to projectDir - const rel = path.relative(path.resolve(projectDir), path.resolve(jsonlPath)); - if (rel.startsWith('..') || path.isAbsolute(rel)) { - return res.status(400).json({ error: 'Invalid path' }); - } - - // Read and parse the JSONL file - let fileContent; - try { - fileContent = await fsPromises.readFile(jsonlPath, 'utf8'); - } catch (error) { - if (error.code === 'ENOENT') { - return res.status(404).json({ error: 'Session file not found', path: jsonlPath }); - } - throw error; // Re-throw other errors to be caught by outer try-catch - } - const lines = fileContent.trim().split('\n'); - - const parsedContextWindow = parseInt(process.env.CONTEXT_WINDOW, 10); - const contextWindow = Number.isFinite(parsedContextWindow) ? parsedContextWindow : 160000; - let inputTokens = 0; - let cacheCreationTokens = 0; - let cacheReadTokens = 0; - - // Find the latest assistant message with usage data (scan from end) - for (let i = lines.length - 1; i >= 0; i--) { - try { - const entry = JSON.parse(lines[i]); - - // Only count assistant messages which have usage data - if (entry.type === 'assistant' && entry.message?.usage) { - const usage = entry.message.usage; - - // Use token counts from latest assistant message only - inputTokens = usage.input_tokens || 0; - cacheCreationTokens = usage.cache_creation_input_tokens || 0; - cacheReadTokens = usage.cache_read_input_tokens || 0; - - break; // Stop after finding the latest assistant message - } - } catch (parseError) { - // Skip lines that can't be parsed - continue; - } - } - - // Calculate total context usage (excluding output_tokens, as per ccusage) - const totalUsed = inputTokens + cacheCreationTokens + cacheReadTokens; - - res.json({ - used: totalUsed, - total: contextWindow, - breakdown: { - input: inputTokens, - cacheCreation: cacheCreationTokens, - cacheRead: cacheReadTokens - } - }); - } catch (error) { - console.error('Error reading session token usage:', error); - res.status(500).json({ error: 'Failed to read session token usage' }); - } -}); - -// Serve React app for all other routes (excluding static files) -app.get('*', (req, res) => { - // Skip requests for static assets (files with extensions) - if (path.extname(req.path)) { - return res.status(404).send('Not found'); - } - - // Only serve index.html for HTML routes, not for static assets - // Static assets should already be handled by express.static middleware above - const indexPath = path.join(__dirname, '../dist/index.html'); - - // Check if dist/index.html exists (production build available) - if (fs.existsSync(indexPath)) { - // Set no-cache headers for HTML to prevent service worker issues - res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); - res.setHeader('Pragma', 'no-cache'); - res.setHeader('Expires', '0'); - res.sendFile(indexPath); - } else { - // In development, redirect to Vite dev server only if dist doesn't exist - const redirectHost = getConnectableHost(req.hostname); - res.redirect(`${req.protocol}://${redirectHost}:${VITE_PORT}`); - } -}); - -// Helper function to convert permissions to rwx format -function permToRwx(perm) { - const r = perm & 4 ? 'r' : '-'; - const w = perm & 2 ? 'w' : '-'; - const x = perm & 1 ? 'x' : '-'; - return r + w + x; -} - -async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = true) { - // Using fsPromises from import - const items = []; - - try { - const entries = await fsPromises.readdir(dirPath, { withFileTypes: true }); - - for (const entry of entries) { - // Debug: log all entries including hidden files - - - // Skip heavy build directories and VCS directories - if (entry.name === 'node_modules' || - entry.name === 'dist' || - entry.name === 'build' || - entry.name === '.git' || - entry.name === '.svn' || - entry.name === '.hg') continue; - - const itemPath = path.join(dirPath, entry.name); - const item = { - name: entry.name, - path: itemPath, - type: entry.isDirectory() ? 'directory' : 'file' - }; - - // Get file stats for additional metadata - try { - const stats = await fsPromises.stat(itemPath); - item.size = stats.size; - item.modified = stats.mtime.toISOString(); - - // Convert permissions to rwx format - const mode = stats.mode; - const ownerPerm = (mode >> 6) & 7; - const groupPerm = (mode >> 3) & 7; - const otherPerm = mode & 7; - item.permissions = ((mode >> 6) & 7).toString() + ((mode >> 3) & 7).toString() + (mode & 7).toString(); - item.permissionsRwx = permToRwx(ownerPerm) + permToRwx(groupPerm) + permToRwx(otherPerm); - } catch (statError) { - // If stat fails, provide default values - item.size = 0; - item.modified = null; - item.permissions = '000'; - item.permissionsRwx = '---------'; - } - - if (entry.isDirectory() && currentDepth < maxDepth) { - // Recursively get subdirectories but limit depth - try { - // Check if we can access the directory before trying to read it - await fsPromises.access(item.path, fs.constants.R_OK); - item.children = await getFileTree(item.path, maxDepth, currentDepth + 1, showHidden); - } catch (e) { - // Silently skip directories we can't access (permission denied, etc.) - item.children = []; - } - } - - items.push(item); - } - } catch (error) { - // Only log non-permission errors to avoid spam - if (error.code !== 'EACCES' && error.code !== 'EPERM') { - console.error('Error reading directory:', error); - } - } - - return items.sort((a, b) => { - if (a.type !== b.type) { - return a.type === 'directory' ? -1 : 1; - } - return a.name.localeCompare(b.name); - }); -} - -const SERVER_PORT = process.env.SERVER_PORT || 3001; -const HOST = process.env.HOST || '0.0.0.0'; -const DISPLAY_HOST = getConnectableHost(HOST); -const VITE_PORT = process.env.VITE_PORT || 5173; - -// Initialize database and start server -async function startServer() { - try { - // Initialize authentication database - await initializeDatabase(); - - // Check if running in production mode (dist folder exists) - const distIndexPath = path.join(__dirname, '../dist/index.html'); - const isProduction = fs.existsSync(distIndexPath); - - // Log Claude implementation mode - console.log(`${c.info('[INFO]')} Using Claude Agents SDK for Claude integration`); - console.log(''); - - if (isProduction) { - console.log(`${c.info('[INFO]')} To run in production mode, go to http://${DISPLAY_HOST}:${SERVER_PORT}`); - } - - console.log(`${c.info('[INFO]')} To run in development mode with hot-module replacement, go to http://${DISPLAY_HOST}:${VITE_PORT}`); - - server.listen(SERVER_PORT, HOST, async () => { - const appInstallPath = path.join(__dirname, '..'); - - console.log(''); - console.log(c.dim('โ•'.repeat(63))); - console.log(` ${c.bright('Claude Code UI Server - Ready')}`); - console.log(c.dim('โ•'.repeat(63))); - console.log(''); - console.log(`${c.info('[INFO]')} Server URL: ${c.bright('http://' + DISPLAY_HOST + ':' + SERVER_PORT)}`); - console.log(`${c.info('[INFO]')} Installed at: ${c.dim(appInstallPath)}`); - console.log(`${c.tip('[TIP]')} Run "cloudcli status" for full configuration details`); - console.log(''); - - // Start watching the projects folder for changes - await setupProjectsWatcher(); - - // Start server-side plugin processes for enabled plugins - startEnabledPluginServers().catch(err => { - console.error('[Plugins] Error during startup:', err.message); - }); - }); - - // Clean up plugin processes on shutdown - const shutdownPlugins = async () => { - await stopAllPlugins(); - process.exit(0); - }; - process.on('SIGTERM', () => void shutdownPlugins()); - process.on('SIGINT', () => void shutdownPlugins()); - } catch (error) { - console.error('[ERROR] Failed to start server:', error); - process.exit(1); - } -} - -startServer(); diff --git a/server/src/config/runtime.ts b/server/src/config/runtime.ts index a5d6f71e..226b7a5f 100644 --- a/server/src/config/runtime.ts +++ b/server/src/config/runtime.ts @@ -14,7 +14,7 @@ export function getRuntimePaths(): RuntimePaths { serverSrcDir, serverDir, projectRoot: path.resolve(serverDir, '..'), - legacyRuntimePath: path.join(serverDir, 'legacy-runtime.js'), + legacyRuntimePath: path.join(serverDir, 'index.js'), bootstrapEntrypointPath: path.join(serverDir, 'dist', 'bootstrap.js'), }; } diff --git a/server/start.js b/server/start.js new file mode 100644 index 00000000..7c4d111b --- /dev/null +++ b/server/start.js @@ -0,0 +1,18 @@ +#!/usr/bin/env node +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const distEntrypoint = path.join(__dirname, 'dist', 'bootstrap.js'); + +if (!fs.existsSync(distEntrypoint)) { + console.error( + '[server] Missing built TypeScript server entrypoint at server/dist/bootstrap.js. ' + + 'Run "npm run server" for development or "npm run server:build" before "npm run server:start".' + ); + process.exit(1); +} + +await import('./dist/bootstrap.js');