claude-code-templates 1.20.3 ā 1.21.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/bin/create-claude-config.js +5 -1
- package/package.json +1 -1
- package/src/analytics-web/components/ActivityHeatmap.js +50 -6
- package/src/analytics-web/components/DashboardPage.js +4 -0
- package/src/analytics-web/services/DataService.js +7 -0
- package/src/analytics.js +67 -20
- package/src/index.js +443 -6
- package/src/sandbox-server.js +555 -0
- package/src/test-activity-data.json +1094 -0
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const express = require('express');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
|
|
9
|
+
const app = express();
|
|
10
|
+
const PORT = process.env.PORT || 3444;
|
|
11
|
+
|
|
12
|
+
// Load .env file from current working directory (where user runs the command)
|
|
13
|
+
function loadEnvFile() {
|
|
14
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
15
|
+
if (fs.existsSync(envPath)) {
|
|
16
|
+
console.log(chalk.blue('š Loading .env file from:'), chalk.gray(envPath));
|
|
17
|
+
|
|
18
|
+
const envContent = fs.readFileSync(envPath, 'utf8');
|
|
19
|
+
const envVars = envContent.split('\n')
|
|
20
|
+
.filter(line => line.trim() && !line.startsWith('#'))
|
|
21
|
+
.reduce((acc, line) => {
|
|
22
|
+
const [key, ...valueParts] = line.split('=');
|
|
23
|
+
if (key && valueParts.length > 0) {
|
|
24
|
+
const value = valueParts.join('=').trim().replace(/^["']|["']$/g, ''); // Remove quotes
|
|
25
|
+
acc[key.trim()] = value;
|
|
26
|
+
}
|
|
27
|
+
return acc;
|
|
28
|
+
}, {});
|
|
29
|
+
|
|
30
|
+
// Set environment variables
|
|
31
|
+
Object.assign(process.env, envVars);
|
|
32
|
+
|
|
33
|
+
const hasE2B = !!process.env.E2B_API_KEY;
|
|
34
|
+
const hasAnthropic = !!process.env.ANTHROPIC_API_KEY;
|
|
35
|
+
|
|
36
|
+
console.log(chalk.green('ā
Environment variables loaded:'));
|
|
37
|
+
console.log(chalk.gray(` ⢠E2B_API_KEY: ${hasE2B ? 'Found' : 'Missing'}`));
|
|
38
|
+
console.log(chalk.gray(` ⢠ANTHROPIC_API_KEY: ${hasAnthropic ? 'Found' : 'Missing'}`));
|
|
39
|
+
|
|
40
|
+
return hasE2B && hasAnthropic;
|
|
41
|
+
} else {
|
|
42
|
+
console.log(chalk.yellow('ā ļø No .env file found in:'), chalk.gray(envPath));
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Load environment variables on startup
|
|
48
|
+
const hasApiKeys = loadEnvFile();
|
|
49
|
+
|
|
50
|
+
// Simple CORS middleware
|
|
51
|
+
app.use((req, res, next) => {
|
|
52
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
53
|
+
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
54
|
+
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');
|
|
55
|
+
if (req.method === 'OPTIONS') {
|
|
56
|
+
res.sendStatus(200);
|
|
57
|
+
} else {
|
|
58
|
+
next();
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// JSON parsing middleware
|
|
63
|
+
app.use(express.json());
|
|
64
|
+
|
|
65
|
+
// Store active tasks
|
|
66
|
+
const activeTasks = new Map();
|
|
67
|
+
|
|
68
|
+
// Serve the sandbox interface at root
|
|
69
|
+
app.get('/', (req, res) => {
|
|
70
|
+
res.sendFile(path.join(__dirname, '../../docs/sandbox-interface.html'));
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Serve static files for CSS, JS, etc. (but not index.html at root)
|
|
74
|
+
app.use('/css', express.static(path.join(__dirname, '../../docs/css')));
|
|
75
|
+
app.use('/js', express.static(path.join(__dirname, '../../docs/js')));
|
|
76
|
+
app.use('/assets', express.static(path.join(__dirname, '../../docs/assets')));
|
|
77
|
+
|
|
78
|
+
// Serve components.json for agent autocomplete
|
|
79
|
+
app.get('/components.json', (req, res) => {
|
|
80
|
+
const componentsPath = path.join(__dirname, '../../docs/components.json');
|
|
81
|
+
if (fs.existsSync(componentsPath)) {
|
|
82
|
+
res.sendFile(componentsPath);
|
|
83
|
+
} else {
|
|
84
|
+
res.status(404).json({ error: 'Components file not found' });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// API endpoint to execute task (local or cloud)
|
|
89
|
+
app.post('/api/execute', async (req, res) => {
|
|
90
|
+
const { prompt, mode = 'local', agent = 'development-team/frontend-developer' } = req.body;
|
|
91
|
+
|
|
92
|
+
if (!prompt || prompt.trim().length < 10) {
|
|
93
|
+
return res.status(400).json({
|
|
94
|
+
success: false,
|
|
95
|
+
error: 'Please provide a detailed prompt (at least 10 characters)'
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const taskId = `task-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
|
|
100
|
+
|
|
101
|
+
// Create task object
|
|
102
|
+
const task = {
|
|
103
|
+
id: taskId,
|
|
104
|
+
title: prompt.substring(0, 60) + (prompt.length > 60 ? '...' : ''),
|
|
105
|
+
prompt: prompt.trim(),
|
|
106
|
+
agent: agent,
|
|
107
|
+
mode: mode,
|
|
108
|
+
status: 'running',
|
|
109
|
+
startTime: new Date(),
|
|
110
|
+
progress: 0,
|
|
111
|
+
output: [],
|
|
112
|
+
sandboxId: null
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
activeTasks.set(taskId, task);
|
|
116
|
+
|
|
117
|
+
// Execute based on mode
|
|
118
|
+
if (mode === 'cloud') {
|
|
119
|
+
executeE2BTask(task);
|
|
120
|
+
} else {
|
|
121
|
+
executeLocalTask(task);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
res.json({
|
|
125
|
+
success: true,
|
|
126
|
+
taskId: taskId,
|
|
127
|
+
message: 'Task started successfully'
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// API endpoint to get task status
|
|
132
|
+
app.get('/api/task/:taskId', (req, res) => {
|
|
133
|
+
const task = activeTasks.get(req.params.taskId);
|
|
134
|
+
if (!task) {
|
|
135
|
+
return res.status(404).json({
|
|
136
|
+
success: false,
|
|
137
|
+
error: 'Task not found'
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
res.json({
|
|
142
|
+
success: true,
|
|
143
|
+
task: {
|
|
144
|
+
id: task.id,
|
|
145
|
+
title: task.title,
|
|
146
|
+
status: task.status,
|
|
147
|
+
progress: task.progress,
|
|
148
|
+
output: task.output.join('\\n'),
|
|
149
|
+
startTime: task.startTime,
|
|
150
|
+
endTime: task.endTime,
|
|
151
|
+
sandboxId: task.sandboxId
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// API endpoint to get all tasks
|
|
157
|
+
app.get('/api/tasks', (req, res) => {
|
|
158
|
+
const tasks = Array.from(activeTasks.values()).map(task => ({
|
|
159
|
+
id: task.id,
|
|
160
|
+
title: task.title,
|
|
161
|
+
status: task.status,
|
|
162
|
+
progress: task.progress,
|
|
163
|
+
startTime: task.startTime,
|
|
164
|
+
endTime: task.endTime,
|
|
165
|
+
sandboxId: task.sandboxId,
|
|
166
|
+
output: task.output.slice(-3).join('\\n') // Last 3 lines for preview
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
res.json({
|
|
170
|
+
success: true,
|
|
171
|
+
tasks: tasks.sort((a, b) => new Date(b.startTime) - new Date(a.startTime))
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
// API endpoint to install agent
|
|
176
|
+
app.post('/api/install-agent', async (req, res) => {
|
|
177
|
+
const { agentName } = req.body;
|
|
178
|
+
|
|
179
|
+
if (!agentName) {
|
|
180
|
+
return res.status(400).json({
|
|
181
|
+
success: false,
|
|
182
|
+
error: 'Agent name is required'
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
console.log(chalk.blue('š§ Installing agent:'), chalk.cyan(agentName));
|
|
188
|
+
|
|
189
|
+
// Use the CLI tool to install the agent
|
|
190
|
+
const child = spawn('npx', ['claude-code-templates@latest', '--agent', agentName, '--yes'], {
|
|
191
|
+
cwd: process.cwd(),
|
|
192
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
193
|
+
shell: true
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
let output = [];
|
|
197
|
+
let error = [];
|
|
198
|
+
|
|
199
|
+
child.stdout.on('data', (data) => {
|
|
200
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
201
|
+
output.push(...lines);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
child.stderr.on('data', (data) => {
|
|
205
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
206
|
+
error.push(...lines);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
child.on('close', (code) => {
|
|
210
|
+
if (code === 0) {
|
|
211
|
+
console.log(chalk.green('ā
Agent installed successfully:'), chalk.cyan(agentName));
|
|
212
|
+
res.json({
|
|
213
|
+
success: true,
|
|
214
|
+
message: `Agent ${agentName} installed successfully`,
|
|
215
|
+
output: output.join('\n')
|
|
216
|
+
});
|
|
217
|
+
} else {
|
|
218
|
+
console.error(chalk.red('ā Agent installation failed:'), chalk.cyan(agentName));
|
|
219
|
+
res.status(500).json({
|
|
220
|
+
success: false,
|
|
221
|
+
error: `Failed to install agent ${agentName}`,
|
|
222
|
+
output: error.join('\n')
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
child.on('error', (error) => {
|
|
228
|
+
console.error(chalk.red('ā Agent installation error:'), error.message);
|
|
229
|
+
res.status(500).json({
|
|
230
|
+
success: false,
|
|
231
|
+
error: `Installation error: ${error.message}`
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error(chalk.red('ā Failed to start agent installation:'), error.message);
|
|
237
|
+
res.status(500).json({
|
|
238
|
+
success: false,
|
|
239
|
+
error: `Failed to start installation: ${error.message}`
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
async function checkAndInstallAgent(agentName, task) {
|
|
245
|
+
// Check if agent exists in .claude directory
|
|
246
|
+
const claudeDir = path.join(process.cwd(), '.claude');
|
|
247
|
+
const agentPath = path.join(claudeDir, 'agents', `${agentName}.md`);
|
|
248
|
+
|
|
249
|
+
if (fs.existsSync(agentPath)) {
|
|
250
|
+
return true; // Agent already exists
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
task.output.push(`š§ Agent ${agentName} not found locally. Installing...`);
|
|
254
|
+
|
|
255
|
+
return new Promise((resolve, reject) => {
|
|
256
|
+
const child = spawn('npx', ['claude-code-templates@latest', '--agent', agentName, '--yes'], {
|
|
257
|
+
cwd: process.cwd(),
|
|
258
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
259
|
+
shell: true
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
child.stdout.on('data', (data) => {
|
|
263
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
264
|
+
lines.forEach(line => {
|
|
265
|
+
task.output.push(`š¦ ${line}`);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
child.stderr.on('data', (data) => {
|
|
270
|
+
const lines = data.toString().split('\n').filter(line => line.trim());
|
|
271
|
+
lines.forEach(line => {
|
|
272
|
+
task.output.push(`ā ļø ${line}`);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
child.on('close', (code) => {
|
|
277
|
+
if (code === 0) {
|
|
278
|
+
task.output.push(`ā
Agent ${agentName} installed successfully`);
|
|
279
|
+
resolve(true);
|
|
280
|
+
} else {
|
|
281
|
+
task.output.push(`ā Failed to install agent ${agentName}`);
|
|
282
|
+
resolve(false);
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
child.on('error', (error) => {
|
|
287
|
+
task.output.push(`ā Installation error: ${error.message}`);
|
|
288
|
+
resolve(false);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async function executeE2BTask(task) {
|
|
294
|
+
try {
|
|
295
|
+
task.output.push('š Initializing E2B sandbox execution...');
|
|
296
|
+
task.progress = 10;
|
|
297
|
+
|
|
298
|
+
// Check and install agent if needed
|
|
299
|
+
if (task.agent !== 'development-team/frontend-developer') {
|
|
300
|
+
task.output.push(`š Checking agent: ${task.agent}`);
|
|
301
|
+
const agentInstalled = await checkAndInstallAgent(task.agent, task);
|
|
302
|
+
if (!agentInstalled) {
|
|
303
|
+
task.status = 'failed';
|
|
304
|
+
task.endTime = new Date();
|
|
305
|
+
task.output.push(`ā Could not install required agent: ${task.agent}`);
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
task.progress = 15;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const e2bLauncherPath = path.join(__dirname, '../components/sandbox/e2b/e2b-launcher.py');
|
|
312
|
+
const agentParam = `--agent=${task.agent} --yes`;
|
|
313
|
+
|
|
314
|
+
// Build command arguments
|
|
315
|
+
const args = [
|
|
316
|
+
e2bLauncherPath,
|
|
317
|
+
task.prompt,
|
|
318
|
+
agentParam
|
|
319
|
+
];
|
|
320
|
+
|
|
321
|
+
// Add API keys from environment if available
|
|
322
|
+
if (process.env.E2B_API_KEY) {
|
|
323
|
+
args.push(process.env.E2B_API_KEY);
|
|
324
|
+
}
|
|
325
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
326
|
+
args.push(process.env.ANTHROPIC_API_KEY);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
task.output.push('š§ Starting Python E2B launcher...');
|
|
330
|
+
task.progress = 20;
|
|
331
|
+
|
|
332
|
+
// Execute the E2B launcher from the user's working directory
|
|
333
|
+
const child = spawn('python3', args, {
|
|
334
|
+
cwd: process.cwd(), // This ensures it runs from where the user executed the command
|
|
335
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
336
|
+
env: {
|
|
337
|
+
...process.env, // Pass all environment variables including loaded ones
|
|
338
|
+
PATH: process.env.PATH
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Handle stdout
|
|
343
|
+
child.stdout.on('data', (data) => {
|
|
344
|
+
const lines = data.toString().split('\\n').filter(line => line.trim());
|
|
345
|
+
lines.forEach(line => {
|
|
346
|
+
task.output.push(line);
|
|
347
|
+
|
|
348
|
+
// Update progress based on output
|
|
349
|
+
if (line.includes('Sandbox created:')) {
|
|
350
|
+
task.sandboxId = line.split('Sandbox created: ')[1] || 'unknown';
|
|
351
|
+
task.progress = 40;
|
|
352
|
+
} else if (line.includes('Installing')) {
|
|
353
|
+
task.progress = 60;
|
|
354
|
+
} else if (line.includes('Executing Claude Code')) {
|
|
355
|
+
task.progress = 80;
|
|
356
|
+
} else if (line.includes('Downloaded:')) {
|
|
357
|
+
task.progress = 95;
|
|
358
|
+
} else if (line.includes('Execution completed successfully')) {
|
|
359
|
+
task.progress = 100;
|
|
360
|
+
task.status = 'completed';
|
|
361
|
+
task.endTime = new Date();
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Handle stderr
|
|
367
|
+
child.stderr.on('data', (data) => {
|
|
368
|
+
const lines = data.toString().split('\\n').filter(line => line.trim());
|
|
369
|
+
lines.forEach(line => {
|
|
370
|
+
task.output.push(`ā ļø ${line}`);
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// Handle process exit
|
|
375
|
+
child.on('close', (code) => {
|
|
376
|
+
if (code === 0) {
|
|
377
|
+
if (task.status !== 'completed') {
|
|
378
|
+
task.status = 'completed';
|
|
379
|
+
task.endTime = new Date();
|
|
380
|
+
task.progress = 100;
|
|
381
|
+
}
|
|
382
|
+
task.output.push('ā
Task completed successfully!');
|
|
383
|
+
} else {
|
|
384
|
+
task.status = 'failed';
|
|
385
|
+
task.endTime = new Date();
|
|
386
|
+
|
|
387
|
+
// Check if it's an API key error
|
|
388
|
+
const outputText = task.output.join(' ');
|
|
389
|
+
if (outputText.includes('E2B API key is required') || outputText.includes('Anthropic API key is required')) {
|
|
390
|
+
task.output.push('ā Missing API keys! Please add E2B_API_KEY and ANTHROPIC_API_KEY to your .env file');
|
|
391
|
+
task.output.push('š Get E2B key: https://e2b.dev/dashboard');
|
|
392
|
+
task.output.push('š Get Anthropic key: https://console.anthropic.com');
|
|
393
|
+
} else {
|
|
394
|
+
task.output.push(`ā Process exited with code: ${code}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Handle process error
|
|
400
|
+
child.on('error', (error) => {
|
|
401
|
+
task.status = 'failed';
|
|
402
|
+
task.endTime = new Date();
|
|
403
|
+
task.output.push(`ā Execution error: ${error.message}`);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
} catch (error) {
|
|
407
|
+
task.status = 'failed';
|
|
408
|
+
task.endTime = new Date();
|
|
409
|
+
task.output.push(`ā Failed to start execution: ${error.message}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
async function executeLocalTask(task) {
|
|
414
|
+
try {
|
|
415
|
+
task.output.push('š„ļø Executing Claude Code locally...');
|
|
416
|
+
task.progress = 10;
|
|
417
|
+
|
|
418
|
+
// Check and install agent if needed
|
|
419
|
+
if (task.agent !== 'development-team/frontend-developer') {
|
|
420
|
+
task.output.push(`š Checking agent: ${task.agent}`);
|
|
421
|
+
const agentInstalled = await checkAndInstallAgent(task.agent, task);
|
|
422
|
+
if (!agentInstalled) {
|
|
423
|
+
task.status = 'failed';
|
|
424
|
+
task.endTime = new Date();
|
|
425
|
+
task.output.push(`ā Could not install required agent: ${task.agent}`);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
task.progress = 15;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
task.output.push('š Checking if Claude Code CLI is available...');
|
|
432
|
+
|
|
433
|
+
// For local execution, we'll include the agent in the prompt if specified
|
|
434
|
+
let finalPrompt = task.prompt;
|
|
435
|
+
if (task.agent && task.agent !== 'development-team/frontend-developer') {
|
|
436
|
+
finalPrompt = `As a ${task.agent.replace('-', ' ')}, ${task.prompt}`;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Execute Claude Code locally with just the prompt
|
|
440
|
+
const child = spawn('claude', [finalPrompt], {
|
|
441
|
+
cwd: process.cwd(),
|
|
442
|
+
stdio: ['ignore', 'pipe', 'pipe'], // Ignore stdin to prevent hanging
|
|
443
|
+
env: {
|
|
444
|
+
...process.env,
|
|
445
|
+
PATH: process.env.PATH
|
|
446
|
+
},
|
|
447
|
+
shell: true, // Use shell to find claude command
|
|
448
|
+
timeout: 300000 // 5 minute timeout
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
task.output.push('š Claude Code execution started');
|
|
452
|
+
task.progress = 30;
|
|
453
|
+
|
|
454
|
+
// Set up timeout to prevent hanging
|
|
455
|
+
const executionTimeout = setTimeout(() => {
|
|
456
|
+
task.output.push('ā° Execution timeout reached (5 minutes)');
|
|
457
|
+
task.output.push('š” This might indicate Claude Code is waiting for input or has hung');
|
|
458
|
+
task.status = 'failed';
|
|
459
|
+
task.endTime = new Date();
|
|
460
|
+
child.kill('SIGTERM');
|
|
461
|
+
}, 300000); // 5 minutes
|
|
462
|
+
|
|
463
|
+
// Handle stdout
|
|
464
|
+
child.stdout.on('data', (data) => {
|
|
465
|
+
const lines = data.toString().split('\\n').filter(line => line.trim());
|
|
466
|
+
lines.forEach(line => {
|
|
467
|
+
task.output.push(line);
|
|
468
|
+
|
|
469
|
+
// Update progress based on output patterns
|
|
470
|
+
if (line.includes('Reading') || line.includes('Analyzing')) {
|
|
471
|
+
task.progress = Math.min(task.progress + 5, 60);
|
|
472
|
+
} else if (line.includes('Writing') || line.includes('Creating')) {
|
|
473
|
+
task.progress = Math.min(task.progress + 10, 90);
|
|
474
|
+
} else if (line.includes('Done') || line.includes('Complete')) {
|
|
475
|
+
task.progress = 95;
|
|
476
|
+
}
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Handle stderr
|
|
481
|
+
child.stderr.on('data', (data) => {
|
|
482
|
+
const lines = data.toString().split('\\n').filter(line => line.trim());
|
|
483
|
+
lines.forEach(line => {
|
|
484
|
+
task.output.push(`ā ļø ${line}`);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
// Handle process exit
|
|
489
|
+
child.on('close', (code) => {
|
|
490
|
+
clearTimeout(executionTimeout); // Clear timeout when process exits
|
|
491
|
+
|
|
492
|
+
if (code === 0) {
|
|
493
|
+
task.status = 'completed';
|
|
494
|
+
task.endTime = new Date();
|
|
495
|
+
task.progress = 100;
|
|
496
|
+
task.output.push('ā
Claude Code execution completed successfully!');
|
|
497
|
+
task.output.push('š Files were created/modified in your current directory');
|
|
498
|
+
} else {
|
|
499
|
+
task.status = 'failed';
|
|
500
|
+
task.endTime = new Date();
|
|
501
|
+
|
|
502
|
+
const outputText = task.output.join(' ');
|
|
503
|
+
if (outputText.includes('claude: command not found') || outputText.includes('not recognized')) {
|
|
504
|
+
task.output.push('ā Claude Code CLI not found!');
|
|
505
|
+
task.output.push('š” Please install Claude Code CLI first:');
|
|
506
|
+
task.output.push('š Visit: https://claude.ai/code');
|
|
507
|
+
} else {
|
|
508
|
+
task.output.push(`ā Process exited with code: ${code}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Handle process error
|
|
514
|
+
child.on('error', (error) => {
|
|
515
|
+
clearTimeout(executionTimeout); // Clear timeout on error
|
|
516
|
+
|
|
517
|
+
task.status = 'failed';
|
|
518
|
+
task.endTime = new Date();
|
|
519
|
+
|
|
520
|
+
if (error.code === 'ENOENT') {
|
|
521
|
+
task.output.push('ā Claude Code CLI not found in PATH!');
|
|
522
|
+
task.output.push('š” Please install Claude Code CLI first:');
|
|
523
|
+
task.output.push('š Visit: https://claude.ai/code');
|
|
524
|
+
task.output.push('š Documentation: https://docs.anthropic.com/claude-code');
|
|
525
|
+
} else {
|
|
526
|
+
task.output.push(`ā Execution error: ${error.message}`);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
} catch (error) {
|
|
531
|
+
task.status = 'failed';
|
|
532
|
+
task.endTime = new Date();
|
|
533
|
+
task.output.push(`ā Failed to start local execution: ${error.message}`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Start server
|
|
538
|
+
app.listen(PORT, () => {
|
|
539
|
+
console.log(chalk.blue('\\nšØ Claude Code Studio Server'));
|
|
540
|
+
console.log(chalk.cyan('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā'));
|
|
541
|
+
console.log(chalk.green(`š Server running on http://localhost:${PORT}`));
|
|
542
|
+
console.log(chalk.gray('š” Local and cloud execution interface ready'));
|
|
543
|
+
|
|
544
|
+
if (hasApiKeys) {
|
|
545
|
+
console.log(chalk.green('\\nā
All API keys are configured and ready!'));
|
|
546
|
+
} else {
|
|
547
|
+
console.log(chalk.yellow('\\nā ļø API Keys Status:'));
|
|
548
|
+
console.log(chalk.gray(` ⢠E2B_API_KEY: ${process.env.E2B_API_KEY ? 'Found' : 'Missing'}`));
|
|
549
|
+
console.log(chalk.gray(` ⢠ANTHROPIC_API_KEY: ${process.env.ANTHROPIC_API_KEY ? 'Found' : 'Missing'}`));
|
|
550
|
+
console.log(chalk.yellow(' ⢠Please add these keys to your .env file'));
|
|
551
|
+
}
|
|
552
|
+
console.log('');
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
module.exports = app;
|