claude-code-workflow 6.2.7 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/.claude/CLAUDE.md +16 -1
  2. package/.claude/workflows/cli-templates/protocols/analysis-protocol.md +11 -4
  3. package/.claude/workflows/cli-templates/protocols/write-protocol.md +10 -75
  4. package/.claude/workflows/cli-tools-usage.md +14 -24
  5. package/.codex/AGENTS.md +51 -1
  6. package/.codex/prompts/compact.md +378 -0
  7. package/.gemini/GEMINI.md +57 -20
  8. package/ccw/dist/cli.d.ts.map +1 -1
  9. package/ccw/dist/cli.js +21 -8
  10. package/ccw/dist/cli.js.map +1 -1
  11. package/ccw/dist/commands/cli.d.ts +2 -0
  12. package/ccw/dist/commands/cli.d.ts.map +1 -1
  13. package/ccw/dist/commands/cli.js +129 -8
  14. package/ccw/dist/commands/cli.js.map +1 -1
  15. package/ccw/dist/commands/hook.d.ts.map +1 -1
  16. package/ccw/dist/commands/hook.js +3 -2
  17. package/ccw/dist/commands/hook.js.map +1 -1
  18. package/ccw/dist/config/litellm-api-config-manager.d.ts +180 -0
  19. package/ccw/dist/config/litellm-api-config-manager.d.ts.map +1 -0
  20. package/ccw/dist/config/litellm-api-config-manager.js +770 -0
  21. package/ccw/dist/config/litellm-api-config-manager.js.map +1 -0
  22. package/ccw/dist/config/provider-models.d.ts +73 -0
  23. package/ccw/dist/config/provider-models.d.ts.map +1 -0
  24. package/ccw/dist/config/provider-models.js +172 -0
  25. package/ccw/dist/config/provider-models.js.map +1 -0
  26. package/ccw/dist/core/cache-manager.d.ts.map +1 -1
  27. package/ccw/dist/core/cache-manager.js +3 -5
  28. package/ccw/dist/core/cache-manager.js.map +1 -1
  29. package/ccw/dist/core/dashboard-generator.d.ts.map +1 -1
  30. package/ccw/dist/core/dashboard-generator.js +3 -1
  31. package/ccw/dist/core/dashboard-generator.js.map +1 -1
  32. package/ccw/dist/core/routes/cli-routes.d.ts.map +1 -1
  33. package/ccw/dist/core/routes/cli-routes.js +169 -0
  34. package/ccw/dist/core/routes/cli-routes.js.map +1 -1
  35. package/ccw/dist/core/routes/codexlens-routes.d.ts.map +1 -1
  36. package/ccw/dist/core/routes/codexlens-routes.js +234 -18
  37. package/ccw/dist/core/routes/codexlens-routes.js.map +1 -1
  38. package/ccw/dist/core/routes/hooks-routes.d.ts.map +1 -1
  39. package/ccw/dist/core/routes/hooks-routes.js +30 -32
  40. package/ccw/dist/core/routes/hooks-routes.js.map +1 -1
  41. package/ccw/dist/core/routes/litellm-api-routes.d.ts +21 -0
  42. package/ccw/dist/core/routes/litellm-api-routes.d.ts.map +1 -0
  43. package/ccw/dist/core/routes/litellm-api-routes.js +780 -0
  44. package/ccw/dist/core/routes/litellm-api-routes.js.map +1 -0
  45. package/ccw/dist/core/routes/litellm-routes.d.ts +20 -0
  46. package/ccw/dist/core/routes/litellm-routes.d.ts.map +1 -0
  47. package/ccw/dist/core/routes/litellm-routes.js +85 -0
  48. package/ccw/dist/core/routes/litellm-routes.js.map +1 -0
  49. package/ccw/dist/core/routes/mcp-routes.js +2 -2
  50. package/ccw/dist/core/routes/mcp-routes.js.map +1 -1
  51. package/ccw/dist/core/routes/status-routes.d.ts.map +1 -1
  52. package/ccw/dist/core/routes/status-routes.js +39 -0
  53. package/ccw/dist/core/routes/status-routes.js.map +1 -1
  54. package/ccw/dist/core/routes/system-routes.js +1 -1
  55. package/ccw/dist/core/routes/system-routes.js.map +1 -1
  56. package/ccw/dist/core/server.d.ts.map +1 -1
  57. package/ccw/dist/core/server.js +15 -1
  58. package/ccw/dist/core/server.js.map +1 -1
  59. package/ccw/dist/mcp-server/index.js +1 -1
  60. package/ccw/dist/mcp-server/index.js.map +1 -1
  61. package/ccw/dist/tools/claude-cli-tools.d.ts +82 -0
  62. package/ccw/dist/tools/claude-cli-tools.d.ts.map +1 -0
  63. package/ccw/dist/tools/claude-cli-tools.js +216 -0
  64. package/ccw/dist/tools/claude-cli-tools.js.map +1 -0
  65. package/ccw/dist/tools/cli-executor.d.ts.map +1 -1
  66. package/ccw/dist/tools/cli-executor.js +76 -14
  67. package/ccw/dist/tools/cli-executor.js.map +1 -1
  68. package/ccw/dist/tools/codex-lens.d.ts +9 -2
  69. package/ccw/dist/tools/codex-lens.d.ts.map +1 -1
  70. package/ccw/dist/tools/codex-lens.js +114 -9
  71. package/ccw/dist/tools/codex-lens.js.map +1 -1
  72. package/ccw/dist/tools/context-cache-store.d.ts +136 -0
  73. package/ccw/dist/tools/context-cache-store.d.ts.map +1 -0
  74. package/ccw/dist/tools/context-cache-store.js +256 -0
  75. package/ccw/dist/tools/context-cache-store.js.map +1 -0
  76. package/ccw/dist/tools/context-cache.d.ts +56 -0
  77. package/ccw/dist/tools/context-cache.d.ts.map +1 -0
  78. package/ccw/dist/tools/context-cache.js +294 -0
  79. package/ccw/dist/tools/context-cache.js.map +1 -0
  80. package/ccw/dist/tools/core-memory.d.ts.map +1 -1
  81. package/ccw/dist/tools/core-memory.js +33 -19
  82. package/ccw/dist/tools/core-memory.js.map +1 -1
  83. package/ccw/dist/tools/index.d.ts.map +1 -1
  84. package/ccw/dist/tools/index.js +2 -0
  85. package/ccw/dist/tools/index.js.map +1 -1
  86. package/ccw/dist/tools/litellm-client.d.ts +85 -0
  87. package/ccw/dist/tools/litellm-client.d.ts.map +1 -0
  88. package/ccw/dist/tools/litellm-client.js +188 -0
  89. package/ccw/dist/tools/litellm-client.js.map +1 -0
  90. package/ccw/dist/tools/litellm-executor.d.ts +34 -0
  91. package/ccw/dist/tools/litellm-executor.d.ts.map +1 -0
  92. package/ccw/dist/tools/litellm-executor.js +192 -0
  93. package/ccw/dist/tools/litellm-executor.js.map +1 -0
  94. package/ccw/dist/tools/pattern-parser.d.ts +55 -0
  95. package/ccw/dist/tools/pattern-parser.d.ts.map +1 -0
  96. package/ccw/dist/tools/pattern-parser.js +237 -0
  97. package/ccw/dist/tools/pattern-parser.js.map +1 -0
  98. package/ccw/dist/tools/smart-search.d.ts +1 -0
  99. package/ccw/dist/tools/smart-search.d.ts.map +1 -1
  100. package/ccw/dist/tools/smart-search.js +117 -41
  101. package/ccw/dist/tools/smart-search.js.map +1 -1
  102. package/ccw/dist/types/litellm-api-config.d.ts +294 -0
  103. package/ccw/dist/types/litellm-api-config.d.ts.map +1 -0
  104. package/ccw/dist/types/litellm-api-config.js +8 -0
  105. package/ccw/dist/types/litellm-api-config.js.map +1 -0
  106. package/ccw/src/cli.ts +258 -244
  107. package/ccw/src/commands/cli.ts +153 -9
  108. package/ccw/src/commands/hook.ts +3 -2
  109. package/ccw/src/config/.litellm-api-config-manager.ts.2025-12-23T11-57-43-727Z.bak +441 -0
  110. package/ccw/src/config/litellm-api-config-manager.ts +1012 -0
  111. package/ccw/src/config/provider-models.ts +222 -0
  112. package/ccw/src/core/cache-manager.ts +292 -294
  113. package/ccw/src/core/dashboard-generator.ts +3 -1
  114. package/ccw/src/core/routes/cli-routes.ts +192 -0
  115. package/ccw/src/core/routes/codexlens-routes.ts +241 -19
  116. package/ccw/src/core/routes/hooks-routes.ts +399 -405
  117. package/ccw/src/core/routes/litellm-api-routes.ts +930 -0
  118. package/ccw/src/core/routes/litellm-routes.ts +107 -0
  119. package/ccw/src/core/routes/mcp-routes.ts +1271 -1271
  120. package/ccw/src/core/routes/status-routes.ts +51 -0
  121. package/ccw/src/core/routes/system-routes.ts +1 -1
  122. package/ccw/src/core/server.ts +15 -1
  123. package/ccw/src/mcp-server/index.ts +1 -1
  124. package/ccw/src/templates/dashboard-css/12-cli-legacy.css +44 -0
  125. package/ccw/src/templates/dashboard-css/31-api-settings.css +2265 -0
  126. package/ccw/src/templates/dashboard-js/components/cli-history.js +15 -8
  127. package/ccw/src/templates/dashboard-js/components/cli-status.js +323 -9
  128. package/ccw/src/templates/dashboard-js/components/navigation.js +329 -313
  129. package/ccw/src/templates/dashboard-js/i18n.js +583 -1
  130. package/ccw/src/templates/dashboard-js/views/api-settings.js +3362 -0
  131. package/ccw/src/templates/dashboard-js/views/cli-manager.js +199 -24
  132. package/ccw/src/templates/dashboard-js/views/codexlens-manager.js +1265 -27
  133. package/ccw/src/templates/dashboard.html +840 -831
  134. package/ccw/src/tools/claude-cli-tools.ts +300 -0
  135. package/ccw/src/tools/cli-executor.ts +83 -14
  136. package/ccw/src/tools/codex-lens.ts +146 -9
  137. package/ccw/src/tools/context-cache-store.ts +368 -0
  138. package/ccw/src/tools/context-cache.ts +393 -0
  139. package/ccw/src/tools/core-memory.ts +33 -19
  140. package/ccw/src/tools/index.ts +2 -0
  141. package/ccw/src/tools/litellm-client.ts +246 -0
  142. package/ccw/src/tools/litellm-executor.ts +241 -0
  143. package/ccw/src/tools/pattern-parser.ts +329 -0
  144. package/ccw/src/tools/smart-search.ts +142 -41
  145. package/ccw/src/types/litellm-api-config.ts +402 -0
  146. package/ccw-litellm/README.md +180 -0
  147. package/ccw-litellm/pyproject.toml +35 -0
  148. package/ccw-litellm/src/ccw_litellm/__init__.py +47 -0
  149. package/ccw-litellm/src/ccw_litellm/__pycache__/__init__.cpython-313.pyc +0 -0
  150. package/ccw-litellm/src/ccw_litellm/__pycache__/cli.cpython-313.pyc +0 -0
  151. package/ccw-litellm/src/ccw_litellm/cli.py +108 -0
  152. package/ccw-litellm/src/ccw_litellm/clients/__init__.py +12 -0
  153. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/__init__.cpython-313.pyc +0 -0
  154. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
  155. package/ccw-litellm/src/ccw_litellm/clients/__pycache__/litellm_llm.cpython-313.pyc +0 -0
  156. package/ccw-litellm/src/ccw_litellm/clients/litellm_embedder.py +251 -0
  157. package/ccw-litellm/src/ccw_litellm/clients/litellm_llm.py +165 -0
  158. package/ccw-litellm/src/ccw_litellm/config/__init__.py +22 -0
  159. package/ccw-litellm/src/ccw_litellm/config/__pycache__/__init__.cpython-313.pyc +0 -0
  160. package/ccw-litellm/src/ccw_litellm/config/__pycache__/loader.cpython-313.pyc +0 -0
  161. package/ccw-litellm/src/ccw_litellm/config/__pycache__/models.cpython-313.pyc +0 -0
  162. package/ccw-litellm/src/ccw_litellm/config/loader.py +316 -0
  163. package/ccw-litellm/src/ccw_litellm/config/models.py +130 -0
  164. package/ccw-litellm/src/ccw_litellm/interfaces/__init__.py +14 -0
  165. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/__init__.cpython-313.pyc +0 -0
  166. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/embedder.cpython-313.pyc +0 -0
  167. package/ccw-litellm/src/ccw_litellm/interfaces/__pycache__/llm.cpython-313.pyc +0 -0
  168. package/ccw-litellm/src/ccw_litellm/interfaces/embedder.py +52 -0
  169. package/ccw-litellm/src/ccw_litellm/interfaces/llm.py +45 -0
  170. package/codex-lens/src/codexlens/__pycache__/config.cpython-313.pyc +0 -0
  171. package/codex-lens/src/codexlens/cli/__pycache__/commands.cpython-313.pyc +0 -0
  172. package/codex-lens/src/codexlens/cli/__pycache__/embedding_manager.cpython-313.pyc +0 -0
  173. package/codex-lens/src/codexlens/cli/__pycache__/model_manager.cpython-313.pyc +0 -0
  174. package/codex-lens/src/codexlens/cli/__pycache__/output.cpython-313.pyc +0 -0
  175. package/codex-lens/src/codexlens/cli/commands.py +378 -23
  176. package/codex-lens/src/codexlens/cli/embedding_manager.py +660 -56
  177. package/codex-lens/src/codexlens/cli/model_manager.py +31 -18
  178. package/codex-lens/src/codexlens/cli/output.py +12 -1
  179. package/codex-lens/src/codexlens/config.py +93 -0
  180. package/codex-lens/src/codexlens/search/__pycache__/chain_search.cpython-313.pyc +0 -0
  181. package/codex-lens/src/codexlens/search/__pycache__/hybrid_search.cpython-313.pyc +0 -0
  182. package/codex-lens/src/codexlens/search/__pycache__/ranking.cpython-313.pyc +0 -0
  183. package/codex-lens/src/codexlens/search/chain_search.py +6 -2
  184. package/codex-lens/src/codexlens/search/hybrid_search.py +44 -21
  185. package/codex-lens/src/codexlens/search/ranking.py +1 -1
  186. package/codex-lens/src/codexlens/semantic/__init__.py +42 -0
  187. package/codex-lens/src/codexlens/semantic/__pycache__/__init__.cpython-313.pyc +0 -0
  188. package/codex-lens/src/codexlens/semantic/__pycache__/base.cpython-313.pyc +0 -0
  189. package/codex-lens/src/codexlens/semantic/__pycache__/chunker.cpython-313.pyc +0 -0
  190. package/codex-lens/src/codexlens/semantic/__pycache__/embedder.cpython-313.pyc +0 -0
  191. package/codex-lens/src/codexlens/semantic/__pycache__/factory.cpython-313.pyc +0 -0
  192. package/codex-lens/src/codexlens/semantic/__pycache__/gpu_support.cpython-313.pyc +0 -0
  193. package/codex-lens/src/codexlens/semantic/__pycache__/litellm_embedder.cpython-313.pyc +0 -0
  194. package/codex-lens/src/codexlens/semantic/__pycache__/vector_store.cpython-313.pyc +0 -0
  195. package/codex-lens/src/codexlens/semantic/base.py +61 -0
  196. package/codex-lens/src/codexlens/semantic/chunker.py +43 -20
  197. package/codex-lens/src/codexlens/semantic/embedder.py +60 -13
  198. package/codex-lens/src/codexlens/semantic/factory.py +98 -0
  199. package/codex-lens/src/codexlens/semantic/gpu_support.py +225 -3
  200. package/codex-lens/src/codexlens/semantic/litellm_embedder.py +144 -0
  201. package/codex-lens/src/codexlens/semantic/rotational_embedder.py +434 -0
  202. package/codex-lens/src/codexlens/semantic/vector_store.py +33 -8
  203. package/codex-lens/src/codexlens/storage/__pycache__/path_mapper.cpython-313.pyc +0 -0
  204. package/codex-lens/src/codexlens/storage/migrations/__pycache__/migration_004_dual_fts.cpython-313.pyc +0 -0
  205. package/codex-lens/src/codexlens/storage/path_mapper.py +27 -1
  206. package/package.json +15 -5
  207. package/.codex/prompts.zip +0 -0
  208. package/ccw/package.json +0 -65
@@ -0,0 +1,780 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { dirname, join as pathJoin } from 'path';
3
+ // Get current module path for package-relative lookups
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ // Package root: routes -> core -> src -> ccw -> package root
7
+ const PACKAGE_ROOT = pathJoin(__dirname, '..', '..', '..', '..');
8
+ import { getAllProviders, getProvider, addProvider, updateProvider, deleteProvider, getAllEndpoints, getEndpoint, addEndpoint, updateEndpoint, deleteEndpoint, getDefaultEndpoint, setDefaultEndpoint, getGlobalCacheSettings, updateGlobalCacheSettings, loadLiteLLMApiConfig, saveLiteLLMYamlConfig, generateLiteLLMYamlConfig, getCodexLensEmbeddingRotation, updateCodexLensEmbeddingRotation, getEmbeddingProvidersForRotation, generateRotationEndpoints, syncCodexLensConfig, getEmbeddingPoolConfig, updateEmbeddingPoolConfig, discoverProvidersForModel, } from '../../config/litellm-api-config-manager.js';
9
+ import { getContextCacheStore } from '../../tools/context-cache-store.js';
10
+ import { getLiteLLMClient } from '../../tools/litellm-client.js';
11
+ // Cache for ccw-litellm status check
12
+ let ccwLitellmStatusCache = {
13
+ data: null,
14
+ timestamp: 0,
15
+ ttl: 5 * 60 * 1000, // 5 minutes
16
+ };
17
+ // Clear cache (call after install)
18
+ export function clearCcwLitellmStatusCache() {
19
+ ccwLitellmStatusCache.data = null;
20
+ ccwLitellmStatusCache.timestamp = 0;
21
+ }
22
+ const PROVIDER_MODELS = {
23
+ openai: [
24
+ { id: 'gpt-4-turbo', name: 'GPT-4 Turbo', provider: 'openai', description: '128K context' },
25
+ { id: 'gpt-4', name: 'GPT-4', provider: 'openai', description: '8K context' },
26
+ { id: 'gpt-3.5-turbo', name: 'GPT-3.5 Turbo', provider: 'openai', description: '16K context' },
27
+ ],
28
+ anthropic: [
29
+ { id: 'claude-3-opus-20240229', name: 'Claude 3 Opus', provider: 'anthropic', description: '200K context' },
30
+ { id: 'claude-3-sonnet-20240229', name: 'Claude 3 Sonnet', provider: 'anthropic', description: '200K context' },
31
+ { id: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', provider: 'anthropic', description: '200K context' },
32
+ ],
33
+ google: [
34
+ { id: 'gemini-pro', name: 'Gemini Pro', provider: 'google', description: '32K context' },
35
+ { id: 'gemini-pro-vision', name: 'Gemini Pro Vision', provider: 'google', description: '16K context' },
36
+ ],
37
+ ollama: [
38
+ { id: 'llama2', name: 'Llama 2', provider: 'ollama', description: 'Local model' },
39
+ { id: 'mistral', name: 'Mistral', provider: 'ollama', description: 'Local model' },
40
+ ],
41
+ azure: [],
42
+ mistral: [
43
+ { id: 'mistral-large-latest', name: 'Mistral Large', provider: 'mistral', description: '32K context' },
44
+ { id: 'mistral-medium-latest', name: 'Mistral Medium', provider: 'mistral', description: '32K context' },
45
+ ],
46
+ deepseek: [
47
+ { id: 'deepseek-chat', name: 'DeepSeek Chat', provider: 'deepseek', description: '64K context' },
48
+ { id: 'deepseek-coder', name: 'DeepSeek Coder', provider: 'deepseek', description: '64K context' },
49
+ ],
50
+ custom: [],
51
+ };
52
+ /**
53
+ * Handle LiteLLM API routes
54
+ * @returns true if route was handled, false otherwise
55
+ */
56
+ export async function handleLiteLLMApiRoutes(ctx) {
57
+ const { pathname, url, req, res, initialPath, handlePostRequest, broadcastToClients } = ctx;
58
+ // ===========================
59
+ // Provider Management Routes
60
+ // ===========================
61
+ // GET /api/litellm-api/providers - List all providers
62
+ if (pathname === '/api/litellm-api/providers' && req.method === 'GET') {
63
+ try {
64
+ const providers = getAllProviders(initialPath);
65
+ res.writeHead(200, { 'Content-Type': 'application/json' });
66
+ res.end(JSON.stringify({ providers, count: providers.length }));
67
+ }
68
+ catch (err) {
69
+ res.writeHead(500, { 'Content-Type': 'application/json' });
70
+ res.end(JSON.stringify({ error: err.message }));
71
+ }
72
+ return true;
73
+ }
74
+ // POST /api/litellm-api/providers - Create provider
75
+ if (pathname === '/api/litellm-api/providers' && req.method === 'POST') {
76
+ handlePostRequest(req, res, async (body) => {
77
+ const providerData = body;
78
+ if (!providerData.name || !providerData.type || !providerData.apiKey) {
79
+ return { error: 'Provider name, type, and apiKey are required', status: 400 };
80
+ }
81
+ try {
82
+ const provider = addProvider(initialPath, providerData);
83
+ broadcastToClients({
84
+ type: 'LITELLM_PROVIDER_CREATED',
85
+ payload: { provider, timestamp: new Date().toISOString() }
86
+ });
87
+ return { success: true, provider };
88
+ }
89
+ catch (err) {
90
+ return { error: err.message, status: 500 };
91
+ }
92
+ });
93
+ return true;
94
+ }
95
+ // GET /api/litellm-api/providers/:id - Get provider by ID
96
+ const providerGetMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
97
+ if (providerGetMatch && req.method === 'GET') {
98
+ const providerId = providerGetMatch[1];
99
+ try {
100
+ const provider = getProvider(initialPath, providerId);
101
+ if (!provider) {
102
+ res.writeHead(404, { 'Content-Type': 'application/json' });
103
+ res.end(JSON.stringify({ error: 'Provider not found' }));
104
+ return true;
105
+ }
106
+ res.writeHead(200, { 'Content-Type': 'application/json' });
107
+ res.end(JSON.stringify(provider));
108
+ }
109
+ catch (err) {
110
+ res.writeHead(500, { 'Content-Type': 'application/json' });
111
+ res.end(JSON.stringify({ error: err.message }));
112
+ }
113
+ return true;
114
+ }
115
+ // PUT /api/litellm-api/providers/:id - Update provider
116
+ const providerUpdateMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
117
+ if (providerUpdateMatch && req.method === 'PUT') {
118
+ const providerId = providerUpdateMatch[1];
119
+ handlePostRequest(req, res, async (body) => {
120
+ const updates = body;
121
+ try {
122
+ const provider = updateProvider(initialPath, providerId, updates);
123
+ broadcastToClients({
124
+ type: 'LITELLM_PROVIDER_UPDATED',
125
+ payload: { provider, timestamp: new Date().toISOString() }
126
+ });
127
+ return { success: true, provider };
128
+ }
129
+ catch (err) {
130
+ return { error: err.message, status: 404 };
131
+ }
132
+ });
133
+ return true;
134
+ }
135
+ // DELETE /api/litellm-api/providers/:id - Delete provider
136
+ const providerDeleteMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)$/);
137
+ if (providerDeleteMatch && req.method === 'DELETE') {
138
+ const providerId = providerDeleteMatch[1];
139
+ try {
140
+ const success = deleteProvider(initialPath, providerId);
141
+ if (!success) {
142
+ res.writeHead(404, { 'Content-Type': 'application/json' });
143
+ res.end(JSON.stringify({ error: 'Provider not found' }));
144
+ return true;
145
+ }
146
+ broadcastToClients({
147
+ type: 'LITELLM_PROVIDER_DELETED',
148
+ payload: { providerId, timestamp: new Date().toISOString() }
149
+ });
150
+ res.writeHead(200, { 'Content-Type': 'application/json' });
151
+ res.end(JSON.stringify({ success: true, message: 'Provider deleted' }));
152
+ }
153
+ catch (err) {
154
+ res.writeHead(500, { 'Content-Type': 'application/json' });
155
+ res.end(JSON.stringify({ error: err.message }));
156
+ }
157
+ return true;
158
+ }
159
+ // POST /api/litellm-api/providers/:id/test - Test provider connection
160
+ const providerTestMatch = pathname.match(/^\/api\/litellm-api\/providers\/([^/]+)\/test$/);
161
+ if (providerTestMatch && req.method === 'POST') {
162
+ const providerId = providerTestMatch[1];
163
+ try {
164
+ const provider = getProvider(initialPath, providerId);
165
+ if (!provider) {
166
+ res.writeHead(404, { 'Content-Type': 'application/json' });
167
+ res.end(JSON.stringify({ success: false, error: 'Provider not found' }));
168
+ return true;
169
+ }
170
+ if (!provider.enabled) {
171
+ res.writeHead(200, { 'Content-Type': 'application/json' });
172
+ res.end(JSON.stringify({ success: false, error: 'Provider is disabled' }));
173
+ return true;
174
+ }
175
+ // Test connection using litellm client
176
+ const client = getLiteLLMClient();
177
+ const available = await client.isAvailable();
178
+ res.writeHead(200, { 'Content-Type': 'application/json' });
179
+ res.end(JSON.stringify({ success: available, provider: provider.type }));
180
+ }
181
+ catch (err) {
182
+ res.writeHead(500, { 'Content-Type': 'application/json' });
183
+ res.end(JSON.stringify({ success: false, error: err.message }));
184
+ }
185
+ return true;
186
+ }
187
+ // ===========================
188
+ // Endpoint Management Routes
189
+ // ===========================
190
+ // GET /api/litellm-api/endpoints - List all endpoints
191
+ if (pathname === '/api/litellm-api/endpoints' && req.method === 'GET') {
192
+ try {
193
+ const endpoints = getAllEndpoints(initialPath);
194
+ res.writeHead(200, { 'Content-Type': 'application/json' });
195
+ res.end(JSON.stringify({ endpoints, count: endpoints.length }));
196
+ }
197
+ catch (err) {
198
+ res.writeHead(500, { 'Content-Type': 'application/json' });
199
+ res.end(JSON.stringify({ error: err.message }));
200
+ }
201
+ return true;
202
+ }
203
+ // POST /api/litellm-api/endpoints - Create endpoint
204
+ if (pathname === '/api/litellm-api/endpoints' && req.method === 'POST') {
205
+ handlePostRequest(req, res, async (body) => {
206
+ const endpointData = body;
207
+ if (!endpointData.id || !endpointData.name || !endpointData.providerId || !endpointData.model) {
208
+ return { error: 'Endpoint id, name, providerId, and model are required', status: 400 };
209
+ }
210
+ try {
211
+ const endpoint = addEndpoint(initialPath, endpointData);
212
+ broadcastToClients({
213
+ type: 'LITELLM_ENDPOINT_CREATED',
214
+ payload: { endpoint, timestamp: new Date().toISOString() }
215
+ });
216
+ return { success: true, endpoint };
217
+ }
218
+ catch (err) {
219
+ return { error: err.message, status: 500 };
220
+ }
221
+ });
222
+ return true;
223
+ }
224
+ // GET /api/litellm-api/endpoints/:id - Get endpoint by ID
225
+ const endpointGetMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
226
+ if (endpointGetMatch && req.method === 'GET') {
227
+ const endpointId = endpointGetMatch[1];
228
+ try {
229
+ const endpoint = getEndpoint(initialPath, endpointId);
230
+ if (!endpoint) {
231
+ res.writeHead(404, { 'Content-Type': 'application/json' });
232
+ res.end(JSON.stringify({ error: 'Endpoint not found' }));
233
+ return true;
234
+ }
235
+ res.writeHead(200, { 'Content-Type': 'application/json' });
236
+ res.end(JSON.stringify(endpoint));
237
+ }
238
+ catch (err) {
239
+ res.writeHead(500, { 'Content-Type': 'application/json' });
240
+ res.end(JSON.stringify({ error: err.message }));
241
+ }
242
+ return true;
243
+ }
244
+ // PUT /api/litellm-api/endpoints/:id - Update endpoint
245
+ const endpointUpdateMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
246
+ if (endpointUpdateMatch && req.method === 'PUT') {
247
+ const endpointId = endpointUpdateMatch[1];
248
+ handlePostRequest(req, res, async (body) => {
249
+ const updates = body;
250
+ try {
251
+ const endpoint = updateEndpoint(initialPath, endpointId, updates);
252
+ broadcastToClients({
253
+ type: 'LITELLM_ENDPOINT_UPDATED',
254
+ payload: { endpoint, timestamp: new Date().toISOString() }
255
+ });
256
+ return { success: true, endpoint };
257
+ }
258
+ catch (err) {
259
+ return { error: err.message, status: 404 };
260
+ }
261
+ });
262
+ return true;
263
+ }
264
+ // DELETE /api/litellm-api/endpoints/:id - Delete endpoint
265
+ const endpointDeleteMatch = pathname.match(/^\/api\/litellm-api\/endpoints\/([^/]+)$/);
266
+ if (endpointDeleteMatch && req.method === 'DELETE') {
267
+ const endpointId = endpointDeleteMatch[1];
268
+ try {
269
+ const success = deleteEndpoint(initialPath, endpointId);
270
+ if (!success) {
271
+ res.writeHead(404, { 'Content-Type': 'application/json' });
272
+ res.end(JSON.stringify({ error: 'Endpoint not found' }));
273
+ return true;
274
+ }
275
+ broadcastToClients({
276
+ type: 'LITELLM_ENDPOINT_DELETED',
277
+ payload: { endpointId, timestamp: new Date().toISOString() }
278
+ });
279
+ res.writeHead(200, { 'Content-Type': 'application/json' });
280
+ res.end(JSON.stringify({ success: true, message: 'Endpoint deleted' }));
281
+ }
282
+ catch (err) {
283
+ res.writeHead(500, { 'Content-Type': 'application/json' });
284
+ res.end(JSON.stringify({ error: err.message }));
285
+ }
286
+ return true;
287
+ }
288
+ // ===========================
289
+ // Model Discovery Routes
290
+ // ===========================
291
+ // GET /api/litellm-api/models/:providerType - Get available models for provider type
292
+ const modelsMatch = pathname.match(/^\/api\/litellm-api\/models\/([^/]+)$/);
293
+ if (modelsMatch && req.method === 'GET') {
294
+ const providerType = modelsMatch[1];
295
+ try {
296
+ const models = PROVIDER_MODELS[providerType];
297
+ if (!models) {
298
+ res.writeHead(404, { 'Content-Type': 'application/json' });
299
+ res.end(JSON.stringify({ error: 'Provider type not found' }));
300
+ return true;
301
+ }
302
+ res.writeHead(200, { 'Content-Type': 'application/json' });
303
+ res.end(JSON.stringify({ providerType, models, count: models.length }));
304
+ }
305
+ catch (err) {
306
+ res.writeHead(500, { 'Content-Type': 'application/json' });
307
+ res.end(JSON.stringify({ error: err.message }));
308
+ }
309
+ return true;
310
+ }
311
+ // ===========================
312
+ // Cache Management Routes
313
+ // ===========================
314
+ // GET /api/litellm-api/cache/stats - Get cache statistics
315
+ if (pathname === '/api/litellm-api/cache/stats' && req.method === 'GET') {
316
+ try {
317
+ const cacheStore = getContextCacheStore();
318
+ const stats = cacheStore.getStatus();
319
+ res.writeHead(200, { 'Content-Type': 'application/json' });
320
+ res.end(JSON.stringify(stats));
321
+ }
322
+ catch (err) {
323
+ res.writeHead(500, { 'Content-Type': 'application/json' });
324
+ res.end(JSON.stringify({ error: err.message }));
325
+ }
326
+ return true;
327
+ }
328
+ // POST /api/litellm-api/cache/clear - Clear cache
329
+ if (pathname === '/api/litellm-api/cache/clear' && req.method === 'POST') {
330
+ try {
331
+ const cacheStore = getContextCacheStore();
332
+ const result = cacheStore.clear();
333
+ broadcastToClients({
334
+ type: 'LITELLM_CACHE_CLEARED',
335
+ payload: { removed: result.removed, timestamp: new Date().toISOString() }
336
+ });
337
+ res.writeHead(200, { 'Content-Type': 'application/json' });
338
+ res.end(JSON.stringify({ success: true, removed: result.removed }));
339
+ }
340
+ catch (err) {
341
+ res.writeHead(500, { 'Content-Type': 'application/json' });
342
+ res.end(JSON.stringify({ error: err.message }));
343
+ }
344
+ return true;
345
+ }
346
+ // ===========================
347
+ // Config Management Routes
348
+ // ===========================
349
+ // GET /api/litellm-api/config - Get full config
350
+ if (pathname === '/api/litellm-api/config' && req.method === 'GET') {
351
+ try {
352
+ const config = loadLiteLLMApiConfig(initialPath);
353
+ res.writeHead(200, { 'Content-Type': 'application/json' });
354
+ res.end(JSON.stringify(config));
355
+ }
356
+ catch (err) {
357
+ res.writeHead(500, { 'Content-Type': 'application/json' });
358
+ res.end(JSON.stringify({ error: err.message }));
359
+ }
360
+ return true;
361
+ }
362
+ // PUT /api/litellm-api/config/cache - Update global cache settings
363
+ if (pathname === '/api/litellm-api/config/cache' && req.method === 'PUT') {
364
+ handlePostRequest(req, res, async (body) => {
365
+ const settings = body;
366
+ try {
367
+ updateGlobalCacheSettings(initialPath, settings);
368
+ const updatedSettings = getGlobalCacheSettings(initialPath);
369
+ broadcastToClients({
370
+ type: 'LITELLM_CACHE_SETTINGS_UPDATED',
371
+ payload: { settings: updatedSettings, timestamp: new Date().toISOString() }
372
+ });
373
+ return { success: true, settings: updatedSettings };
374
+ }
375
+ catch (err) {
376
+ return { error: err.message, status: 500 };
377
+ }
378
+ });
379
+ return true;
380
+ }
381
+ // PUT /api/litellm-api/config/default-endpoint - Set default endpoint
382
+ if (pathname === '/api/litellm-api/config/default-endpoint' && req.method === 'PUT') {
383
+ handlePostRequest(req, res, async (body) => {
384
+ const { endpointId } = body;
385
+ try {
386
+ setDefaultEndpoint(initialPath, endpointId);
387
+ const defaultEndpoint = getDefaultEndpoint(initialPath);
388
+ broadcastToClients({
389
+ type: 'LITELLM_DEFAULT_ENDPOINT_UPDATED',
390
+ payload: { endpointId, defaultEndpoint, timestamp: new Date().toISOString() }
391
+ });
392
+ return { success: true, defaultEndpoint };
393
+ }
394
+ catch (err) {
395
+ return { error: err.message, status: 500 };
396
+ }
397
+ });
398
+ return true;
399
+ }
400
+ // ===========================
401
+ // Config Sync Routes
402
+ // ===========================
403
+ // POST /api/litellm-api/config/sync - Sync UI config to ccw_litellm YAML config
404
+ if (pathname === '/api/litellm-api/config/sync' && req.method === 'POST') {
405
+ try {
406
+ const yamlPath = saveLiteLLMYamlConfig(initialPath);
407
+ res.writeHead(200, { 'Content-Type': 'application/json' });
408
+ res.end(JSON.stringify({
409
+ success: true,
410
+ message: 'Config synced to ccw_litellm',
411
+ yamlPath,
412
+ }));
413
+ }
414
+ catch (err) {
415
+ res.writeHead(500, { 'Content-Type': 'application/json' });
416
+ res.end(JSON.stringify({ error: err.message }));
417
+ }
418
+ return true;
419
+ }
420
+ // GET /api/litellm-api/config/yaml-preview - Preview YAML config without saving
421
+ if (pathname === '/api/litellm-api/config/yaml-preview' && req.method === 'GET') {
422
+ try {
423
+ const yamlConfig = generateLiteLLMYamlConfig(initialPath);
424
+ res.writeHead(200, { 'Content-Type': 'application/json' });
425
+ res.end(JSON.stringify({
426
+ success: true,
427
+ config: yamlConfig,
428
+ }));
429
+ }
430
+ catch (err) {
431
+ res.writeHead(500, { 'Content-Type': 'application/json' });
432
+ res.end(JSON.stringify({ error: err.message }));
433
+ }
434
+ return true;
435
+ }
436
+ // ===========================
437
+ // CCW-LiteLLM Package Management
438
+ // ===========================
439
+ // GET /api/litellm-api/ccw-litellm/status - Check ccw-litellm installation status
440
+ // Supports ?refresh=true to bypass cache
441
+ if (pathname === '/api/litellm-api/ccw-litellm/status' && req.method === 'GET') {
442
+ const forceRefresh = url.searchParams.get('refresh') === 'true';
443
+ // Check cache first (unless force refresh)
444
+ if (!forceRefresh && ccwLitellmStatusCache.data &&
445
+ Date.now() - ccwLitellmStatusCache.timestamp < ccwLitellmStatusCache.ttl) {
446
+ res.writeHead(200, { 'Content-Type': 'application/json' });
447
+ res.end(JSON.stringify(ccwLitellmStatusCache.data));
448
+ return true;
449
+ }
450
+ // Async check - use pip show for more reliable detection
451
+ try {
452
+ const { exec } = await import('child_process');
453
+ const { promisify } = await import('util');
454
+ const execAsync = promisify(exec);
455
+ let result = { installed: false };
456
+ // Method 1: Try pip show ccw-litellm (most reliable)
457
+ try {
458
+ const { stdout } = await execAsync('pip show ccw-litellm', {
459
+ timeout: 10000,
460
+ windowsHide: true,
461
+ shell: true,
462
+ });
463
+ // Parse version from pip show output
464
+ const versionMatch = stdout.match(/Version:\s*(.+)/i);
465
+ if (versionMatch) {
466
+ result = { installed: true, version: versionMatch[1].trim() };
467
+ console.log(`[ccw-litellm status] Found via pip show: ${result.version}`);
468
+ }
469
+ }
470
+ catch (pipErr) {
471
+ console.log('[ccw-litellm status] pip show failed, trying python import...');
472
+ // Method 2: Fallback to Python import
473
+ const pythonExecutables = ['python', 'python3', 'py'];
474
+ for (const pythonExe of pythonExecutables) {
475
+ try {
476
+ // Use simpler Python code without complex quotes
477
+ const { stdout } = await execAsync(`${pythonExe} -c "import ccw_litellm; print(ccw_litellm.__version__)"`, {
478
+ timeout: 5000,
479
+ windowsHide: true,
480
+ shell: true,
481
+ });
482
+ const version = stdout.trim();
483
+ if (version) {
484
+ result = { installed: true, version };
485
+ console.log(`[ccw-litellm status] Found with ${pythonExe}: ${version}`);
486
+ break;
487
+ }
488
+ }
489
+ catch (err) {
490
+ result.error = err.message;
491
+ console.log(`[ccw-litellm status] ${pythonExe} failed:`, result.error.substring(0, 100));
492
+ }
493
+ }
494
+ }
495
+ // Update cache
496
+ ccwLitellmStatusCache = {
497
+ data: result,
498
+ timestamp: Date.now(),
499
+ ttl: 5 * 60 * 1000,
500
+ };
501
+ res.writeHead(200, { 'Content-Type': 'application/json' });
502
+ res.end(JSON.stringify(result));
503
+ }
504
+ catch (err) {
505
+ const errorResult = { installed: false, error: err.message };
506
+ res.writeHead(200, { 'Content-Type': 'application/json' });
507
+ res.end(JSON.stringify(errorResult));
508
+ }
509
+ return true;
510
+ }
511
+ // ===========================
512
+ // CodexLens Embedding Rotation Routes
513
+ // ===========================
514
+ // GET /api/litellm-api/codexlens/rotation - Get rotation config
515
+ if (pathname === '/api/litellm-api/codexlens/rotation' && req.method === 'GET') {
516
+ try {
517
+ const rotationConfig = getCodexLensEmbeddingRotation(initialPath);
518
+ const availableProviders = getEmbeddingProvidersForRotation(initialPath);
519
+ res.writeHead(200, { 'Content-Type': 'application/json' });
520
+ res.end(JSON.stringify({
521
+ rotationConfig: rotationConfig || null,
522
+ availableProviders,
523
+ }));
524
+ }
525
+ catch (err) {
526
+ res.writeHead(500, { 'Content-Type': 'application/json' });
527
+ res.end(JSON.stringify({ error: err.message }));
528
+ }
529
+ return true;
530
+ }
531
+ // PUT /api/litellm-api/codexlens/rotation - Update rotation config
532
+ if (pathname === '/api/litellm-api/codexlens/rotation' && req.method === 'PUT') {
533
+ handlePostRequest(req, res, async (body) => {
534
+ const rotationConfig = body;
535
+ try {
536
+ const { syncResult } = updateCodexLensEmbeddingRotation(initialPath, rotationConfig || undefined);
537
+ broadcastToClients({
538
+ type: 'CODEXLENS_ROTATION_UPDATED',
539
+ payload: { rotationConfig, syncResult, timestamp: new Date().toISOString() }
540
+ });
541
+ return { success: true, rotationConfig, syncResult };
542
+ }
543
+ catch (err) {
544
+ return { error: err.message, status: 500 };
545
+ }
546
+ });
547
+ return true;
548
+ }
549
+ // GET /api/litellm-api/codexlens/rotation/endpoints - Get generated rotation endpoints
550
+ if (pathname === '/api/litellm-api/codexlens/rotation/endpoints' && req.method === 'GET') {
551
+ try {
552
+ const endpoints = generateRotationEndpoints(initialPath);
553
+ res.writeHead(200, { 'Content-Type': 'application/json' });
554
+ res.end(JSON.stringify({
555
+ endpoints,
556
+ count: endpoints.length,
557
+ }));
558
+ }
559
+ catch (err) {
560
+ res.writeHead(500, { 'Content-Type': 'application/json' });
561
+ res.end(JSON.stringify({ error: err.message }));
562
+ }
563
+ return true;
564
+ }
565
+ // POST /api/litellm-api/codexlens/rotation/sync - Manually sync rotation config to CodexLens
566
+ if (pathname === '/api/litellm-api/codexlens/rotation/sync' && req.method === 'POST') {
567
+ try {
568
+ const syncResult = syncCodexLensConfig(initialPath);
569
+ if (syncResult.success) {
570
+ broadcastToClients({
571
+ type: 'CODEXLENS_CONFIG_SYNCED',
572
+ payload: { ...syncResult, timestamp: new Date().toISOString() }
573
+ });
574
+ }
575
+ res.writeHead(200, { 'Content-Type': 'application/json' });
576
+ res.end(JSON.stringify(syncResult));
577
+ }
578
+ catch (err) {
579
+ res.writeHead(500, { 'Content-Type': 'application/json' });
580
+ res.end(JSON.stringify({ success: false, message: err.message }));
581
+ }
582
+ return true;
583
+ }
584
+ // ===========================
585
+ // Embedding Pool Routes (New Generic API)
586
+ // ===========================
587
+ // GET /api/litellm-api/embedding-pool - Get pool config and available models
588
+ if (pathname === '/api/litellm-api/embedding-pool' && req.method === 'GET') {
589
+ try {
590
+ const poolConfig = getEmbeddingPoolConfig(initialPath);
591
+ // Get list of all available embedding models from all providers
592
+ const config = loadLiteLLMApiConfig(initialPath);
593
+ const availableModels = [];
594
+ const modelMap = new Map();
595
+ for (const provider of config.providers) {
596
+ if (!provider.enabled || !provider.embeddingModels)
597
+ continue;
598
+ for (const model of provider.embeddingModels) {
599
+ if (!model.enabled)
600
+ continue;
601
+ const key = model.id;
602
+ if (modelMap.has(key)) {
603
+ modelMap.get(key).providers.push(provider.name);
604
+ }
605
+ else {
606
+ modelMap.set(key, {
607
+ modelId: model.id,
608
+ modelName: model.name,
609
+ providers: [provider.name],
610
+ });
611
+ }
612
+ }
613
+ }
614
+ availableModels.push(...Array.from(modelMap.values()));
615
+ res.writeHead(200, { 'Content-Type': 'application/json' });
616
+ res.end(JSON.stringify({
617
+ poolConfig: poolConfig || null,
618
+ availableModels,
619
+ }));
620
+ }
621
+ catch (err) {
622
+ res.writeHead(500, { 'Content-Type': 'application/json' });
623
+ res.end(JSON.stringify({ error: err.message }));
624
+ }
625
+ return true;
626
+ }
627
+ // PUT /api/litellm-api/embedding-pool - Update pool config
628
+ if (pathname === '/api/litellm-api/embedding-pool' && req.method === 'PUT') {
629
+ handlePostRequest(req, res, async (body) => {
630
+ const poolConfig = body;
631
+ try {
632
+ const { syncResult } = updateEmbeddingPoolConfig(initialPath, poolConfig || undefined);
633
+ broadcastToClients({
634
+ type: 'EMBEDDING_POOL_UPDATED',
635
+ payload: { poolConfig, syncResult, timestamp: new Date().toISOString() }
636
+ });
637
+ return { success: true, poolConfig, syncResult };
638
+ }
639
+ catch (err) {
640
+ return { error: err.message, status: 500 };
641
+ }
642
+ });
643
+ return true;
644
+ }
645
+ // GET /api/litellm-api/embedding-pool/discover/:model - Preview auto-discovery results
646
+ const discoverMatch = pathname.match(/^\/api\/litellm-api\/embedding-pool\/discover\/([^/]+)$/);
647
+ if (discoverMatch && req.method === 'GET') {
648
+ const targetModel = decodeURIComponent(discoverMatch[1]);
649
+ try {
650
+ const discovered = discoverProvidersForModel(initialPath, targetModel);
651
+ res.writeHead(200, { 'Content-Type': 'application/json' });
652
+ res.end(JSON.stringify({
653
+ targetModel,
654
+ discovered,
655
+ count: discovered.length,
656
+ }));
657
+ }
658
+ catch (err) {
659
+ res.writeHead(500, { 'Content-Type': 'application/json' });
660
+ res.end(JSON.stringify({ error: err.message }));
661
+ }
662
+ return true;
663
+ }
664
+ // POST /api/litellm-api/ccw-litellm/install - Install ccw-litellm package
665
+ if (pathname === '/api/litellm-api/ccw-litellm/install' && req.method === 'POST') {
666
+ handlePostRequest(req, res, async () => {
667
+ try {
668
+ const { spawn } = await import('child_process');
669
+ const path = await import('path');
670
+ const fs = await import('fs');
671
+ // Try to find ccw-litellm package in distribution
672
+ const possiblePaths = [
673
+ path.join(initialPath, 'ccw-litellm'),
674
+ path.join(initialPath, '..', 'ccw-litellm'),
675
+ path.join(process.cwd(), 'ccw-litellm'),
676
+ path.join(PACKAGE_ROOT, 'ccw-litellm'), // npm package internal path
677
+ ];
678
+ let packagePath = '';
679
+ for (const p of possiblePaths) {
680
+ const pyproject = path.join(p, 'pyproject.toml');
681
+ if (fs.existsSync(pyproject)) {
682
+ packagePath = p;
683
+ break;
684
+ }
685
+ }
686
+ if (!packagePath) {
687
+ // Try pip install from PyPI as fallback
688
+ return new Promise((resolve) => {
689
+ const proc = spawn('pip', ['install', 'ccw-litellm'], { shell: true, timeout: 300000 });
690
+ let output = '';
691
+ let error = '';
692
+ proc.stdout?.on('data', (data) => { output += data.toString(); });
693
+ proc.stderr?.on('data', (data) => { error += data.toString(); });
694
+ proc.on('close', (code) => {
695
+ if (code === 0) {
696
+ // Clear status cache after successful installation
697
+ clearCcwLitellmStatusCache();
698
+ resolve({ success: true, message: 'ccw-litellm installed from PyPI' });
699
+ }
700
+ else {
701
+ resolve({ success: false, error: error || 'Installation failed' });
702
+ }
703
+ });
704
+ proc.on('error', (err) => resolve({ success: false, error: err.message }));
705
+ });
706
+ }
707
+ // Install from local package
708
+ return new Promise((resolve) => {
709
+ const proc = spawn('pip', ['install', '-e', packagePath], { shell: true, timeout: 300000 });
710
+ let output = '';
711
+ let error = '';
712
+ proc.stdout?.on('data', (data) => { output += data.toString(); });
713
+ proc.stderr?.on('data', (data) => { error += data.toString(); });
714
+ proc.on('close', (code) => {
715
+ if (code === 0) {
716
+ // Clear status cache after successful installation
717
+ clearCcwLitellmStatusCache();
718
+ // Broadcast installation event
719
+ broadcastToClients({
720
+ type: 'CCW_LITELLM_INSTALLED',
721
+ payload: { timestamp: new Date().toISOString() }
722
+ });
723
+ resolve({ success: true, message: 'ccw-litellm installed successfully', path: packagePath });
724
+ }
725
+ else {
726
+ resolve({ success: false, error: error || output || 'Installation failed' });
727
+ }
728
+ });
729
+ proc.on('error', (err) => resolve({ success: false, error: err.message }));
730
+ });
731
+ }
732
+ catch (err) {
733
+ return { success: false, error: err.message };
734
+ }
735
+ });
736
+ return true;
737
+ }
738
+ // POST /api/litellm-api/ccw-litellm/uninstall - Uninstall ccw-litellm package
739
+ if (pathname === '/api/litellm-api/ccw-litellm/uninstall' && req.method === 'POST') {
740
+ handlePostRequest(req, res, async () => {
741
+ try {
742
+ const { spawn } = await import('child_process');
743
+ return new Promise((resolve) => {
744
+ const proc = spawn('pip', ['uninstall', '-y', 'ccw-litellm'], { shell: true, timeout: 120000 });
745
+ let output = '';
746
+ let error = '';
747
+ proc.stdout?.on('data', (data) => { output += data.toString(); });
748
+ proc.stderr?.on('data', (data) => { error += data.toString(); });
749
+ proc.on('close', (code) => {
750
+ // Clear status cache after uninstallation attempt
751
+ clearCcwLitellmStatusCache();
752
+ if (code === 0) {
753
+ broadcastToClients({
754
+ type: 'CCW_LITELLM_UNINSTALLED',
755
+ payload: { timestamp: new Date().toISOString() }
756
+ });
757
+ resolve({ success: true, message: 'ccw-litellm uninstalled successfully' });
758
+ }
759
+ else {
760
+ // Check if package was not installed
761
+ if (error.includes('not installed') || output.includes('not installed')) {
762
+ resolve({ success: true, message: 'ccw-litellm was not installed' });
763
+ }
764
+ else {
765
+ resolve({ success: false, error: error || output || 'Uninstallation failed' });
766
+ }
767
+ }
768
+ });
769
+ proc.on('error', (err) => resolve({ success: false, error: err.message }));
770
+ });
771
+ }
772
+ catch (err) {
773
+ return { success: false, error: err.message };
774
+ }
775
+ });
776
+ return true;
777
+ }
778
+ return false;
779
+ }
780
+ //# sourceMappingURL=litellm-api-routes.js.map