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');