agentstudio 0.1.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.
- package/.env +15 -0
- package/README.md +85 -0
- package/dist/bin/agentstudio.d.ts +3 -0
- package/dist/bin/agentstudio.d.ts.map +1 -0
- package/dist/bin/agentstudio.js +141 -0
- package/dist/bin/agentstudio.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +7 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +21 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +804 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +60 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/files.d.ts.map +1 -0
- package/dist/routes/files.js +301 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +652 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/media.d.ts +5 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +117 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/slides.d.ts +4 -0
- package/dist/routes/slides.d.ts.map +1 -0
- package/dist/routes/slides.js +146 -0
- package/dist/routes/slides.js.map +1 -0
- package/dist/services/claudeSession.d.ts +83 -0
- package/dist/services/claudeSession.d.ts.map +1 -0
- package/dist/services/claudeSession.js +255 -0
- package/dist/services/claudeSession.js.map +1 -0
- package/dist/services/messageQueue.d.ts +31 -0
- package/dist/services/messageQueue.d.ts.map +1 -0
- package/dist/services/messageQueue.js +67 -0
- package/dist/services/messageQueue.js.map +1 -0
- package/dist/services/sessionManager.d.ts +132 -0
- package/dist/services/sessionManager.d.ts.map +1 -0
- package/dist/services/sessionManager.js +439 -0
- package/dist/services/sessionManager.js.map +1 -0
- package/dist/types/claude-history.d.ts +48 -0
- package/dist/types/claude-history.d.ts.map +1 -0
- package/dist/types/claude-history.js +2 -0
- package/dist/types/claude-history.js.map +1 -0
- package/dist/types/claude-versions.d.ts +31 -0
- package/dist/types/claude-versions.d.ts.map +1 -0
- package/dist/types/claude-versions.js +2 -0
- package/dist/types/claude-versions.js.map +1 -0
- package/dist/types/commands.d.ts +32 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +2 -0
- package/dist/types/commands.js.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +150 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/subagents.d.ts +88 -0
- package/dist/types/subagents.d.ts.map +1 -0
- package/dist/types/subagents.js +2 -0
- package/dist/types/subagents.js.map +1 -0
- package/dist/utils/agentStorage.d.ts +19 -0
- package/dist/utils/agentStorage.d.ts.map +1 -0
- package/dist/utils/agentStorage.js +110 -0
- package/dist/utils/agentStorage.js.map +1 -0
- package/dist/utils/claudeVersionStorage.d.ts +33 -0
- package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
- package/dist/utils/claudeVersionStorage.js +168 -0
- package/dist/utils/claudeVersionStorage.js.map +1 -0
- package/dist/utils/jwt.d.ts +15 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +28 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/projectMetadataStorage.d.ts +21 -0
- package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
- package/dist/utils/projectMetadataStorage.js +68 -0
- package/dist/utils/projectMetadataStorage.js.map +1 -0
- package/frontend/dist/index.html +86 -0
- package/package.json +66 -0
- package/src/bin/agentstudio.ts +161 -0
- package/src/index.ts +100 -0
- package/src/middleware/auth.ts +26 -0
- package/src/routes/agents.ts +885 -0
- package/src/routes/auth.ts +73 -0
- package/src/routes/commands.ts.bak +441 -0
- package/src/routes/files.ts +352 -0
- package/src/routes/mcp.ts +751 -0
- package/src/routes/media.ts +140 -0
- package/src/routes/projects.ts.bak +601 -0
- package/src/routes/sessions.ts.bak +809 -0
- package/src/routes/settings.ts.bak +718 -0
- package/src/routes/slides.ts +170 -0
- package/src/routes/subagents.ts.bak +364 -0
- package/src/services/claudeSession.ts +293 -0
- package/src/services/messageQueue.ts +71 -0
- package/src/services/sessionManager.ts +532 -0
- package/src/types/claude-history.ts +50 -0
- package/src/types/claude-versions.ts +33 -0
- package/src/types/commands.ts +35 -0
- package/src/types/index.ts +248 -0
- package/src/types/subagents.ts +106 -0
- package/src/utils/agentStorage.ts +126 -0
- package/src/utils/claudeVersionStorage.ts +199 -0
- package/src/utils/jwt.ts +36 -0
- package/src/utils/projectMetadataStorage.ts +86 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
import express, { Router } from 'express';
|
|
2
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { homedir } from 'os';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { promisify } from 'util';
|
|
7
|
+
import {
|
|
8
|
+
getAllVersions,
|
|
9
|
+
getDefaultVersionId,
|
|
10
|
+
setDefaultVersion,
|
|
11
|
+
createVersion,
|
|
12
|
+
updateVersion,
|
|
13
|
+
deleteVersion,
|
|
14
|
+
initializeSystemVersion
|
|
15
|
+
} from '../utils/claudeVersionStorage.js';
|
|
16
|
+
import { ClaudeVersionCreate, ClaudeVersionUpdate } from '../types/claude-versions.js';
|
|
17
|
+
|
|
18
|
+
const router: Router = express.Router();
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
|
|
21
|
+
// Package manager detection and utilities
|
|
22
|
+
const detectPackageManagers = async () => {
|
|
23
|
+
const managers = {
|
|
24
|
+
npm: false,
|
|
25
|
+
pnpm: false,
|
|
26
|
+
yarn: false
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Check npm
|
|
30
|
+
try {
|
|
31
|
+
await execAsync('npm --version');
|
|
32
|
+
managers.npm = true;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
// npm not available
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Check pnpm
|
|
38
|
+
try {
|
|
39
|
+
await execAsync('pnpm --version');
|
|
40
|
+
managers.pnpm = true;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
// pnpm not available
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check yarn
|
|
46
|
+
try {
|
|
47
|
+
await execAsync('yarn --version');
|
|
48
|
+
managers.yarn = true;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
// yarn not available
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return managers;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// Detect which package manager installed Claude Code
|
|
57
|
+
const detectClaudeCodeInstallationSource = async () => {
|
|
58
|
+
try {
|
|
59
|
+
// Get Claude Code executable path
|
|
60
|
+
const { stdout: claudePath } = await execAsync('which claude');
|
|
61
|
+
if (!claudePath) return null;
|
|
62
|
+
|
|
63
|
+
const cleanPath = claudePath.trim();
|
|
64
|
+
console.log('Claude Code executable path:', cleanPath);
|
|
65
|
+
|
|
66
|
+
// Skip local node_modules paths - we want global installation
|
|
67
|
+
if (cleanPath.includes('node_modules/.bin')) {
|
|
68
|
+
console.log('Skipping local node_modules path, looking for global installation');
|
|
69
|
+
|
|
70
|
+
// Try to find global installation by checking PATH without local node_modules
|
|
71
|
+
try {
|
|
72
|
+
const { stdout: allClaudes } = await execAsync('which -a claude');
|
|
73
|
+
const claudes = allClaudes.trim().split('\n');
|
|
74
|
+
|
|
75
|
+
// Find the first non-local installation
|
|
76
|
+
for (const claudePathOption of claudes) {
|
|
77
|
+
if (!claudePathOption.includes('node_modules/.bin')) {
|
|
78
|
+
console.log('Found global Claude installation:', claudePathOption);
|
|
79
|
+
const globalPath = claudePathOption.trim();
|
|
80
|
+
|
|
81
|
+
// Check for pnpm patterns
|
|
82
|
+
if (globalPath.includes('/pnpm/') || globalPath.includes('pnpm')) {
|
|
83
|
+
console.log('Detected pnpm installation from global path');
|
|
84
|
+
return 'pnpm';
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Continue with other checks using this global path
|
|
88
|
+
const cleanGlobalPath = globalPath;
|
|
89
|
+
|
|
90
|
+
// Check for yarn patterns
|
|
91
|
+
if (cleanGlobalPath.includes('/yarn/') || cleanGlobalPath.includes('yarn')) {
|
|
92
|
+
console.log('Detected yarn installation from global path');
|
|
93
|
+
return 'yarn';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check npm patterns
|
|
97
|
+
try {
|
|
98
|
+
const { stdout: npmPrefix } = await execAsync('npm config get prefix');
|
|
99
|
+
if (cleanGlobalPath.startsWith(npmPrefix.trim())) {
|
|
100
|
+
console.log('Detected npm installation from global path');
|
|
101
|
+
return 'npm';
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// Ignore npm prefix error
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
break; // Use the first non-local path found
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.log('Could not find alternative Claude installations');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check for pnpm patterns
|
|
116
|
+
if (cleanPath.includes('/pnpm/') || cleanPath.includes('pnpm')) {
|
|
117
|
+
console.log('Detected pnpm installation from path');
|
|
118
|
+
return 'pnpm';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Check for yarn patterns
|
|
122
|
+
if (cleanPath.includes('/yarn/') || cleanPath.includes('yarn')) {
|
|
123
|
+
return 'yarn';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check npm patterns - could be in node_modules or npm prefix
|
|
127
|
+
try {
|
|
128
|
+
const { stdout: npmPrefix } = await execAsync('npm config get prefix');
|
|
129
|
+
if (cleanPath.startsWith(npmPrefix.trim())) {
|
|
130
|
+
return 'npm';
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// Ignore npm prefix error
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Additional checks for npm global installation patterns
|
|
137
|
+
if (cleanPath.includes('/node_modules/.bin/') ||
|
|
138
|
+
cleanPath.includes('/npm/') ||
|
|
139
|
+
cleanPath.includes('/.npm/')) {
|
|
140
|
+
return 'npm';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Fallback: check for the presence of Claude Code in different package managers' global directories
|
|
144
|
+
const managers = await detectPackageManagers();
|
|
145
|
+
|
|
146
|
+
// Check pnpm global directory
|
|
147
|
+
if (managers.pnpm) {
|
|
148
|
+
try {
|
|
149
|
+
const { stdout: pnpmRoot } = await execAsync('pnpm root -g');
|
|
150
|
+
const pnpmClaudePath = `${pnpmRoot.trim()}/@anthropic-ai/claude-code`;
|
|
151
|
+
await execAsync(`test -d "${pnpmClaudePath}"`);
|
|
152
|
+
return 'pnpm';
|
|
153
|
+
} catch (error) {
|
|
154
|
+
// Claude Code not found in pnpm global
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Check yarn global directory
|
|
159
|
+
if (managers.yarn) {
|
|
160
|
+
try {
|
|
161
|
+
const { stdout: yarnGlobalDir } = await execAsync('yarn global dir');
|
|
162
|
+
const yarnClaudePath = `${yarnGlobalDir.trim()}/node_modules/@anthropic-ai/claude-code`;
|
|
163
|
+
await execAsync(`test -d "${yarnClaudePath}"`);
|
|
164
|
+
return 'yarn';
|
|
165
|
+
} catch (error) {
|
|
166
|
+
// Claude Code not found in yarn global
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Check npm global directory
|
|
171
|
+
if (managers.npm) {
|
|
172
|
+
try {
|
|
173
|
+
const { stdout: npmPrefix } = await execAsync('npm config get prefix');
|
|
174
|
+
const npmClaudePath = `${npmPrefix.trim()}/lib/node_modules/@anthropic-ai/claude-code`;
|
|
175
|
+
await execAsync(`test -d "${npmClaudePath}"`);
|
|
176
|
+
return 'npm';
|
|
177
|
+
} catch (error) {
|
|
178
|
+
// Claude Code not found in npm global
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return null;
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error('Error detecting Claude Code installation source:', error);
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Get preferred package manager for global installs
|
|
190
|
+
const getPreferredPackageManager = async () => {
|
|
191
|
+
// First try to detect which package manager installed Claude Code
|
|
192
|
+
const claudeInstallSource = await detectClaudeCodeInstallationSource();
|
|
193
|
+
if (claudeInstallSource) {
|
|
194
|
+
return claudeInstallSource;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Fallback to priority order if detection fails
|
|
198
|
+
const managers = await detectPackageManagers();
|
|
199
|
+
if (managers.pnpm) return 'pnpm';
|
|
200
|
+
if (managers.yarn) return 'yarn';
|
|
201
|
+
if (managers.npm) return 'npm';
|
|
202
|
+
|
|
203
|
+
return null;
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// Get package manager version
|
|
207
|
+
const getPackageManagerVersion = async (manager: string) => {
|
|
208
|
+
try {
|
|
209
|
+
const { stdout } = await execAsync(`${manager} --version`);
|
|
210
|
+
return stdout.trim();
|
|
211
|
+
} catch (error) {
|
|
212
|
+
return 'Not found';
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Get user's home directory
|
|
217
|
+
const getUserHomeDir = () => homedir();
|
|
218
|
+
const getGlobalMemoryPath = () => join(getUserHomeDir(), '.claude', 'CLAUDE.md');
|
|
219
|
+
|
|
220
|
+
// GET /api/settings/global-memory - Read global memory file
|
|
221
|
+
router.get('/global-memory', async (req, res) => {
|
|
222
|
+
try {
|
|
223
|
+
const filePath = getGlobalMemoryPath();
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const content = await readFile(filePath, 'utf-8');
|
|
227
|
+
res.type('text/plain').send(content);
|
|
228
|
+
} catch (error: any) {
|
|
229
|
+
if (error.code === 'ENOENT') {
|
|
230
|
+
// File doesn't exist, return empty content
|
|
231
|
+
res.type('text/plain').send('');
|
|
232
|
+
} else {
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('Error reading global memory:', error);
|
|
238
|
+
res.status(500).json({
|
|
239
|
+
error: 'Failed to read global memory',
|
|
240
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// POST /api/settings/global-memory - Write global memory file
|
|
246
|
+
router.post('/global-memory', express.text({ type: 'text/plain' }), async (req, res) => {
|
|
247
|
+
try {
|
|
248
|
+
const content = req.body;
|
|
249
|
+
|
|
250
|
+
if (typeof content !== 'string') {
|
|
251
|
+
return res.status(400).json({
|
|
252
|
+
error: 'Invalid content type',
|
|
253
|
+
message: 'Content must be a string'
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const filePath = getGlobalMemoryPath();
|
|
258
|
+
|
|
259
|
+
await writeFile(filePath, content, 'utf-8');
|
|
260
|
+
|
|
261
|
+
res.json({
|
|
262
|
+
success: true,
|
|
263
|
+
message: 'Global memory saved successfully',
|
|
264
|
+
filePath
|
|
265
|
+
});
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error('Error writing global memory:', error);
|
|
268
|
+
res.status(500).json({
|
|
269
|
+
error: 'Failed to save global memory',
|
|
270
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
// GET /api/settings/global-memory/path - Get the path to the global memory file
|
|
276
|
+
router.get('/global-memory/path', (req, res) => {
|
|
277
|
+
try {
|
|
278
|
+
const filePath = getGlobalMemoryPath();
|
|
279
|
+
res.json({
|
|
280
|
+
path: filePath,
|
|
281
|
+
homeDir: getUserHomeDir()
|
|
282
|
+
});
|
|
283
|
+
} catch (error) {
|
|
284
|
+
console.error('Error getting global memory path:', error);
|
|
285
|
+
res.status(500).json({
|
|
286
|
+
error: 'Failed to get file path',
|
|
287
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// GET /api/settings/versions - Get version information for Claude Code, Node.js, and package managers
|
|
293
|
+
router.get('/versions', async (req, res) => {
|
|
294
|
+
try {
|
|
295
|
+
const availableManagers = await detectPackageManagers();
|
|
296
|
+
const preferredManager = await getPreferredPackageManager();
|
|
297
|
+
|
|
298
|
+
const claudeInstallSource = await detectClaudeCodeInstallationSource();
|
|
299
|
+
|
|
300
|
+
const versions: any = {
|
|
301
|
+
nodejs: null,
|
|
302
|
+
packageManagers: {},
|
|
303
|
+
preferredManager: preferredManager,
|
|
304
|
+
claudeInstallSource: claudeInstallSource,
|
|
305
|
+
claudeCode: null,
|
|
306
|
+
lastChecked: new Date().toISOString()
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// Get Node.js version
|
|
310
|
+
try {
|
|
311
|
+
const { stdout: nodeVersion } = await execAsync('node --version');
|
|
312
|
+
versions.nodejs = nodeVersion.trim();
|
|
313
|
+
} catch (error) {
|
|
314
|
+
console.error('Failed to get Node.js version:', error);
|
|
315
|
+
versions.nodejs = 'Not found';
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Get all available package manager versions
|
|
319
|
+
for (const [manager, available] of Object.entries(availableManagers)) {
|
|
320
|
+
if (available) {
|
|
321
|
+
versions.packageManagers[manager] = await getPackageManagerVersion(manager);
|
|
322
|
+
} else {
|
|
323
|
+
versions.packageManagers[manager] = 'Not installed';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Get Claude Code version using the correct global path
|
|
328
|
+
try {
|
|
329
|
+
let claudeCommand = 'claude --version';
|
|
330
|
+
|
|
331
|
+
// If we detected a specific installation source, try to use the correct path
|
|
332
|
+
if (claudeInstallSource) {
|
|
333
|
+
try {
|
|
334
|
+
const { stdout: claudePath } = await execAsync('which claude');
|
|
335
|
+
if (claudePath && claudePath.includes('node_modules/.bin')) {
|
|
336
|
+
// We're getting the local version, try to find the global one
|
|
337
|
+
const { stdout: allClaudes } = await execAsync('which -a claude');
|
|
338
|
+
const claudes = allClaudes.trim().split('\n');
|
|
339
|
+
|
|
340
|
+
for (const claudePathOption of claudes) {
|
|
341
|
+
if (!claudePathOption.includes('node_modules/.bin')) {
|
|
342
|
+
claudeCommand = `"${claudePathOption.trim()}" --version`;
|
|
343
|
+
console.log('Using global Claude path for version:', claudePathOption.trim());
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
} catch (error) {
|
|
349
|
+
// Fallback to default command
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const { stdout: claudeVersion } = await execAsync(claudeCommand);
|
|
354
|
+
versions.claudeCode = claudeVersion.trim();
|
|
355
|
+
console.log('Claude Code version detected:', claudeVersion.trim());
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error('Failed to get Claude Code version:', error);
|
|
358
|
+
versions.claudeCode = 'Not installed';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
res.json(versions);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Error getting version information:', error);
|
|
364
|
+
res.status(500).json({
|
|
365
|
+
error: 'Failed to get version information',
|
|
366
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// POST /api/settings/update-claude - Update Claude Code using preferred package manager
|
|
372
|
+
router.post('/update-claude', async (req, res) => {
|
|
373
|
+
try {
|
|
374
|
+
const preferredManager = await getPreferredPackageManager();
|
|
375
|
+
|
|
376
|
+
if (!preferredManager) {
|
|
377
|
+
return res.status(400).json({
|
|
378
|
+
success: false,
|
|
379
|
+
error: 'No package manager available',
|
|
380
|
+
message: 'Please install npm, pnpm, or yarn to update Claude Code'
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Different commands for different package managers
|
|
385
|
+
let updateCommand: string;
|
|
386
|
+
switch (preferredManager) {
|
|
387
|
+
case 'pnpm':
|
|
388
|
+
updateCommand = 'pnpm add -g @anthropic-ai/claude-code@latest';
|
|
389
|
+
break;
|
|
390
|
+
case 'yarn':
|
|
391
|
+
updateCommand = 'yarn global add @anthropic-ai/claude-code@latest';
|
|
392
|
+
break;
|
|
393
|
+
case 'npm':
|
|
394
|
+
default:
|
|
395
|
+
updateCommand = 'npm update -g @anthropic-ai/claude-code';
|
|
396
|
+
break;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
console.log(`Updating Claude Code using ${preferredManager}: ${updateCommand}`);
|
|
400
|
+
const { stdout, stderr } = await execAsync(updateCommand);
|
|
401
|
+
|
|
402
|
+
res.json({
|
|
403
|
+
success: true,
|
|
404
|
+
message: `Claude Code update completed using ${preferredManager}`,
|
|
405
|
+
packageManager: preferredManager,
|
|
406
|
+
command: updateCommand,
|
|
407
|
+
output: stdout,
|
|
408
|
+
error: stderr || null
|
|
409
|
+
});
|
|
410
|
+
} catch (error: any) {
|
|
411
|
+
console.error('Failed to update Claude Code:', error);
|
|
412
|
+
res.status(500).json({
|
|
413
|
+
success: false,
|
|
414
|
+
error: 'Failed to update Claude Code',
|
|
415
|
+
message: error.message,
|
|
416
|
+
output: error.stdout || null,
|
|
417
|
+
stderr: error.stderr || null
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// Claude 版本管理 API
|
|
423
|
+
|
|
424
|
+
// GET /api/settings/claude-versions - 获取所有 Claude 版本
|
|
425
|
+
router.get('/claude-versions', async (req, res) => {
|
|
426
|
+
try {
|
|
427
|
+
// 首先确保系统版本存在
|
|
428
|
+
try {
|
|
429
|
+
const { stdout: claudePath } = await execAsync('which claude');
|
|
430
|
+
if (claudePath) {
|
|
431
|
+
await initializeSystemVersion(claudePath.trim());
|
|
432
|
+
}
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.warn('No system claude found:', error);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const versions = await getAllVersions();
|
|
438
|
+
const defaultVersionId = await getDefaultVersionId();
|
|
439
|
+
|
|
440
|
+
res.json({
|
|
441
|
+
versions,
|
|
442
|
+
defaultVersionId
|
|
443
|
+
});
|
|
444
|
+
} catch (error) {
|
|
445
|
+
console.error('Error getting Claude versions:', error);
|
|
446
|
+
res.status(500).json({
|
|
447
|
+
error: 'Failed to get Claude versions',
|
|
448
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// POST /api/settings/claude-versions - 创建新的 Claude 版本
|
|
454
|
+
router.post('/claude-versions', async (req, res) => {
|
|
455
|
+
try {
|
|
456
|
+
const data: ClaudeVersionCreate = req.body;
|
|
457
|
+
|
|
458
|
+
// 验证必填字段
|
|
459
|
+
if (!data.name || !data.alias) {
|
|
460
|
+
return res.status(400).json({
|
|
461
|
+
error: 'Missing required fields',
|
|
462
|
+
message: 'name and alias are required'
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
const version = await createVersion(data);
|
|
467
|
+
res.json(version);
|
|
468
|
+
} catch (error) {
|
|
469
|
+
console.error('Error creating Claude version:', error);
|
|
470
|
+
const status = error instanceof Error && error.message.includes('已存在') ? 409 : 500;
|
|
471
|
+
res.status(status).json({
|
|
472
|
+
error: 'Failed to create Claude version',
|
|
473
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
// PUT /api/settings/claude-versions/:id - 更新 Claude 版本
|
|
479
|
+
router.put('/claude-versions/:id', async (req, res) => {
|
|
480
|
+
try {
|
|
481
|
+
const { id } = req.params;
|
|
482
|
+
const data: ClaudeVersionUpdate = req.body;
|
|
483
|
+
|
|
484
|
+
const version = await updateVersion(id, data);
|
|
485
|
+
res.json(version);
|
|
486
|
+
} catch (error) {
|
|
487
|
+
console.error('Error updating Claude version:', error);
|
|
488
|
+
const status = error instanceof Error && (
|
|
489
|
+
error.message.includes('不存在') ||
|
|
490
|
+
error.message.includes('已存在') ||
|
|
491
|
+
error.message.includes('不允许')
|
|
492
|
+
) ? 400 : 500;
|
|
493
|
+
res.status(status).json({
|
|
494
|
+
error: 'Failed to update Claude version',
|
|
495
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
// DELETE /api/settings/claude-versions/:id - 删除 Claude 版本
|
|
501
|
+
router.delete('/claude-versions/:id', async (req, res) => {
|
|
502
|
+
try {
|
|
503
|
+
const { id } = req.params;
|
|
504
|
+
|
|
505
|
+
await deleteVersion(id);
|
|
506
|
+
res.json({ success: true });
|
|
507
|
+
} catch (error) {
|
|
508
|
+
console.error('Error deleting Claude version:', error);
|
|
509
|
+
const status = error instanceof Error && (
|
|
510
|
+
error.message.includes('不存在') ||
|
|
511
|
+
error.message.includes('不允许')
|
|
512
|
+
) ? 400 : 500;
|
|
513
|
+
res.status(status).json({
|
|
514
|
+
error: 'Failed to delete Claude version',
|
|
515
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// PUT /api/settings/claude-versions/:id/set-default - 设置默认版本
|
|
521
|
+
router.put('/claude-versions/:id/set-default', async (req, res) => {
|
|
522
|
+
try {
|
|
523
|
+
const { id } = req.params;
|
|
524
|
+
|
|
525
|
+
await setDefaultVersion(id);
|
|
526
|
+
res.json({ success: true });
|
|
527
|
+
} catch (error) {
|
|
528
|
+
console.error('Error setting default Claude version:', error);
|
|
529
|
+
const status = error instanceof Error && error.message.includes('不存在') ? 400 : 500;
|
|
530
|
+
res.status(status).json({
|
|
531
|
+
error: 'Failed to set default Claude version',
|
|
532
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// POST /api/settings/claude-versions/detect - 检测 Claude CLI 安装
|
|
538
|
+
router.post('/claude-versions/detect', async (req, res) => {
|
|
539
|
+
try {
|
|
540
|
+
const result = {
|
|
541
|
+
userInstalled: false,
|
|
542
|
+
systemInstalled: false,
|
|
543
|
+
userPath: null as string | null,
|
|
544
|
+
systemPath: null as string | null,
|
|
545
|
+
version: null as string | null,
|
|
546
|
+
packageManager: null as string | null
|
|
547
|
+
};
|
|
548
|
+
|
|
549
|
+
// 检测用户自己安装的 Claude CLI (全局安装)
|
|
550
|
+
try {
|
|
551
|
+
const { stdout: claudePath } = await execAsync('which claude');
|
|
552
|
+
if (claudePath) {
|
|
553
|
+
const cleanPath = claudePath.trim();
|
|
554
|
+
|
|
555
|
+
// 检测是否是系统 npm 包自带的 (在项目 node_modules 中)
|
|
556
|
+
const projectRoot = process.cwd();
|
|
557
|
+
const isSystemPackage = cleanPath.includes(`${projectRoot}/node_modules`);
|
|
558
|
+
|
|
559
|
+
if (isSystemPackage) {
|
|
560
|
+
result.systemInstalled = true;
|
|
561
|
+
result.systemPath = cleanPath;
|
|
562
|
+
} else {
|
|
563
|
+
result.userInstalled = true;
|
|
564
|
+
result.userPath = cleanPath;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 获取版本
|
|
568
|
+
try {
|
|
569
|
+
const { stdout: version } = await execAsync(`"${cleanPath}" --version`);
|
|
570
|
+
result.version = version.trim();
|
|
571
|
+
} catch (error) {
|
|
572
|
+
console.error('Failed to get Claude version:', error);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// 检测包管理器
|
|
576
|
+
if (result.userInstalled) {
|
|
577
|
+
result.packageManager = await detectClaudeCodeInstallationSource();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
} catch (error) {
|
|
581
|
+
console.log('No Claude CLI found in PATH');
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// 如果没有找到全局安装的,检查系统 npm 包(在 backend/node_modules 中)
|
|
585
|
+
if (!result.systemInstalled) {
|
|
586
|
+
try {
|
|
587
|
+
const projectRoot = process.cwd();
|
|
588
|
+
// 在 workspace 项目中,backend 的依赖在 backend/node_modules
|
|
589
|
+
const systemClaudePath = join(projectRoot, 'backend', 'node_modules', '.bin', 'claude');
|
|
590
|
+
|
|
591
|
+
// 检查文件是否存在(.bin/claude 是符号链接,用 -e 而不是 -f)
|
|
592
|
+
const { stdout } = await execAsync(`test -e "${systemClaudePath}" && echo "exists"`);
|
|
593
|
+
if (stdout.trim() === 'exists') {
|
|
594
|
+
result.systemInstalled = true;
|
|
595
|
+
result.systemPath = systemClaudePath;
|
|
596
|
+
|
|
597
|
+
// 尝试获取版本
|
|
598
|
+
if (!result.version) {
|
|
599
|
+
try {
|
|
600
|
+
const { stdout: version } = await execAsync(`"${systemClaudePath}" --version`);
|
|
601
|
+
result.version = version.trim();
|
|
602
|
+
} catch (error) {
|
|
603
|
+
console.error('Failed to get system package version:', error);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
} catch (error) {
|
|
608
|
+
console.log('System package not found:', error);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
res.json(result);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
console.error('Error detecting Claude CLI:', error);
|
|
615
|
+
res.status(500).json({
|
|
616
|
+
error: 'Failed to detect Claude CLI',
|
|
617
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// POST /api/settings/claude-versions/init-system - 初始化系统版本
|
|
623
|
+
router.post('/claude-versions/init-system', async (req, res) => {
|
|
624
|
+
try {
|
|
625
|
+
const {
|
|
626
|
+
useUserInstalled,
|
|
627
|
+
authToken,
|
|
628
|
+
skipAuthToken
|
|
629
|
+
} = req.body;
|
|
630
|
+
|
|
631
|
+
let claudePath: string | null = null;
|
|
632
|
+
|
|
633
|
+
// 根据用户选择获取 Claude 路径
|
|
634
|
+
if (useUserInstalled) {
|
|
635
|
+
// 使用用户自己安装的
|
|
636
|
+
try {
|
|
637
|
+
const { stdout } = await execAsync('which claude');
|
|
638
|
+
const cleanPath = stdout.trim();
|
|
639
|
+
const projectRoot = process.cwd();
|
|
640
|
+
|
|
641
|
+
// 确保不是项目内的
|
|
642
|
+
if (!cleanPath.includes(`${projectRoot}/node_modules`)) {
|
|
643
|
+
claudePath = cleanPath;
|
|
644
|
+
} else {
|
|
645
|
+
return res.status(400).json({
|
|
646
|
+
error: 'Invalid selection',
|
|
647
|
+
message: '未找到用户全局安装的 Claude CLI'
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
} catch (error) {
|
|
651
|
+
return res.status(400).json({
|
|
652
|
+
error: 'Claude CLI not found',
|
|
653
|
+
message: '未找到用户安装的 Claude CLI'
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
} else {
|
|
657
|
+
// 使用系统 npm 包自带的(在 backend/node_modules 中)
|
|
658
|
+
try {
|
|
659
|
+
const projectRoot = process.cwd();
|
|
660
|
+
const systemClaudePath = join(projectRoot, 'backend', 'node_modules', '.bin', 'claude');
|
|
661
|
+
const { stdout } = await execAsync(`test -e "${systemClaudePath}" && echo "exists"`);
|
|
662
|
+
|
|
663
|
+
if (stdout.trim() === 'exists') {
|
|
664
|
+
claudePath = systemClaudePath;
|
|
665
|
+
} else {
|
|
666
|
+
return res.status(400).json({
|
|
667
|
+
error: 'System Claude not found',
|
|
668
|
+
message: '系统 npm 包中未找到 Claude CLI'
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
} catch (error) {
|
|
672
|
+
return res.status(400).json({
|
|
673
|
+
error: 'System Claude not found',
|
|
674
|
+
message: '系统 npm 包中未找到 Claude CLI'
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (!claudePath) {
|
|
680
|
+
return res.status(400).json({
|
|
681
|
+
error: 'No Claude CLI found',
|
|
682
|
+
message: '未找到可用的 Claude CLI'
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// 准备环境变量
|
|
687
|
+
const environmentVariables: Record<string, string> = {};
|
|
688
|
+
|
|
689
|
+
if (authToken && !skipAuthToken) {
|
|
690
|
+
environmentVariables.ANTHROPIC_AUTH_TOKEN = authToken;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// 创建或更新系统版本
|
|
694
|
+
const storage = await import('../utils/claudeVersionStorage.js');
|
|
695
|
+
let systemVersion = await storage.initializeSystemVersion(claudePath);
|
|
696
|
+
|
|
697
|
+
// 更新环境变量
|
|
698
|
+
if (Object.keys(environmentVariables).length > 0) {
|
|
699
|
+
systemVersion = await storage.updateVersion(systemVersion.id, {
|
|
700
|
+
environmentVariables
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
res.json({
|
|
705
|
+
success: true,
|
|
706
|
+
version: systemVersion,
|
|
707
|
+
message: '系统版本初始化成功'
|
|
708
|
+
});
|
|
709
|
+
} catch (error) {
|
|
710
|
+
console.error('Error initializing system version:', error);
|
|
711
|
+
res.status(500).json({
|
|
712
|
+
error: 'Failed to initialize system version',
|
|
713
|
+
message: error instanceof Error ? error.message : 'Unknown error'
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
export default router;
|