devbonzai 2.0.3 → 2.0.5

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 (2) hide show
  1. package/cli.js +97 -4
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -64,7 +64,7 @@ const express = require('./node_modules/express');
64
64
  const cors = require('./node_modules/cors');
65
65
  const fs = require('fs');
66
66
  const path = require('path');
67
- const { exec, spawn } = require('child_process');
67
+ const { exec, spawn, execSync } = require('child_process');
68
68
  let babelParser = null;
69
69
  try {
70
70
  babelParser = require('./node_modules/@babel/parser');
@@ -92,6 +92,7 @@ app.get('/', (req, res) => {
92
92
  'POST /move': 'Move file or folder (body: {source, destination})',
93
93
  'POST /open-cursor': 'Open Cursor (body: {path, line?})',
94
94
  'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
95
+ 'POST /revert_job': 'Revert to a previous commit (body: {beforeCommit})',
95
96
  'POST /shutdown': 'Gracefully shutdown the server'
96
97
  },
97
98
  example: 'Try: /list or /read?path=README.md'
@@ -1284,6 +1285,49 @@ app.post('/prompt_agent', (req, res) => {
1284
1285
  return res.status(400).json({ error: 'prompt required' });
1285
1286
  }
1286
1287
 
1288
+ // Capture beforeCommit
1289
+ let beforeCommit = '';
1290
+ try {
1291
+ beforeCommit = execSync('git rev-parse HEAD', { cwd: ROOT }).toString().trim();
1292
+ console.log('🔵 [prompt_agent] beforeCommit:', beforeCommit);
1293
+ } catch (e) {
1294
+ console.log('⚠️ [prompt_agent] Could not get beforeCommit:', e.message);
1295
+ }
1296
+
1297
+ // Capture initial state of modified files (files already dirty before job starts)
1298
+ const initiallyModifiedFiles = new Set();
1299
+ try {
1300
+ const initialStatus = execSync('git status --short', { cwd: ROOT }).toString();
1301
+ initialStatus.split('\\n').filter(Boolean).forEach(line => {
1302
+ const filePath = line.substring(3).trim();
1303
+ if (filePath) initiallyModifiedFiles.add(filePath);
1304
+ });
1305
+ console.log('🔵 [prompt_agent] Initially modified files:', Array.from(initiallyModifiedFiles));
1306
+ } catch (e) {
1307
+ console.log('⚠️ [prompt_agent] Could not get initial status:', e.message);
1308
+ }
1309
+
1310
+ // Set up file change tracking - only track NEW changes during job
1311
+ const changedFiles = new Set();
1312
+ const pollInterval = setInterval(() => {
1313
+ try {
1314
+ const status = execSync('git status --short', { cwd: ROOT }).toString();
1315
+ status.split('\\n').filter(Boolean).forEach(line => {
1316
+ const filePath = line.substring(3).trim(); // Remove status prefix (XY + space)
1317
+ // Only add if this file was NOT already modified before the job started
1318
+ if (filePath && !initiallyModifiedFiles.has(filePath)) {
1319
+ const wasNew = !changedFiles.has(filePath);
1320
+ changedFiles.add(filePath);
1321
+ if (wasNew) {
1322
+ console.log('📁 [prompt_agent] New file changed:', filePath);
1323
+ }
1324
+ }
1325
+ });
1326
+ } catch (e) {
1327
+ // Ignore git status errors
1328
+ }
1329
+ }, 500);
1330
+
1287
1331
  // Configurable timeout (default 5 minutes)
1288
1332
  const timeoutMs = parseInt(req.body.timeout) || 5 * 60 * 1000;
1289
1333
  let timeoutId = null;
@@ -1312,6 +1356,7 @@ app.post('/prompt_agent', (req, res) => {
1312
1356
  timeoutId = setTimeout(() => {
1313
1357
  if (!responseSent && proc && !proc.killed) {
1314
1358
  console.log('⏱️ [prompt_agent] Timeout reached, killing process...');
1359
+ clearInterval(pollInterval);
1315
1360
  proc.kill('SIGTERM');
1316
1361
 
1317
1362
  // Force kill after a short grace period if SIGTERM doesn't work
@@ -1329,7 +1374,10 @@ app.post('/prompt_agent', (req, res) => {
1329
1374
  message: \`cursor-agent exceeded timeout of \${timeoutMs / 1000} seconds\`,
1330
1375
  code: -1,
1331
1376
  stdout,
1332
- stderr
1377
+ stderr,
1378
+ changedFiles: Array.from(changedFiles),
1379
+ beforeCommit,
1380
+ afterCommit: ''
1333
1381
  });
1334
1382
  }
1335
1383
  }
@@ -1349,6 +1397,7 @@ app.post('/prompt_agent', (req, res) => {
1349
1397
 
1350
1398
  proc.on('error', (error) => {
1351
1399
  console.log('❌ [prompt_agent] Process error:', error.message);
1400
+ clearInterval(pollInterval);
1352
1401
  if (timeoutId) clearTimeout(timeoutId);
1353
1402
  if (!responseSent) {
1354
1403
  responseSent = true;
@@ -1361,8 +1410,19 @@ app.post('/prompt_agent', (req, res) => {
1361
1410
  console.log('🔵 [prompt_agent] stdout length:', stdout.length);
1362
1411
  console.log('🔵 [prompt_agent] stderr length:', stderr.length);
1363
1412
 
1413
+ // Stop polling for file changes
1414
+ clearInterval(pollInterval);
1364
1415
  if (timeoutId) clearTimeout(timeoutId);
1365
1416
 
1417
+ // Capture afterCommit
1418
+ let afterCommit = '';
1419
+ try {
1420
+ afterCommit = execSync('git rev-parse HEAD', { cwd: ROOT }).toString().trim();
1421
+ console.log('🔵 [prompt_agent] afterCommit:', afterCommit);
1422
+ } catch (e) {
1423
+ console.log('⚠️ [prompt_agent] Could not get afterCommit:', e.message);
1424
+ }
1425
+
1366
1426
  if (!responseSent) {
1367
1427
  responseSent = true;
1368
1428
  // Check if process was killed due to timeout
@@ -1372,19 +1432,52 @@ app.post('/prompt_agent', (req, res) => {
1372
1432
  message: signal === 'SIGTERM' ? 'Process was terminated due to timeout' : 'Process was force killed',
1373
1433
  code: code || -1,
1374
1434
  stdout,
1375
- stderr
1435
+ stderr,
1436
+ changedFiles: Array.from(changedFiles),
1437
+ beforeCommit,
1438
+ afterCommit
1376
1439
  });
1377
1440
  } else {
1378
1441
  res.json({
1379
1442
  code,
1380
1443
  stdout,
1381
- stderr
1444
+ stderr,
1445
+ changedFiles: Array.from(changedFiles),
1446
+ beforeCommit,
1447
+ afterCommit
1382
1448
  });
1383
1449
  }
1384
1450
  }
1385
1451
  });
1386
1452
  });
1387
1453
 
1454
+ // Revert job endpoint to reset to a previous commit
1455
+ app.post('/revert_job', (req, res) => {
1456
+ console.log('🔵 [revert_job] Endpoint hit');
1457
+ const { beforeCommit } = req.body;
1458
+
1459
+ if (!beforeCommit || typeof beforeCommit !== 'string') {
1460
+ console.log('❌ [revert_job] Error: beforeCommit required');
1461
+ return res.status(400).json({ error: 'beforeCommit required' });
1462
+ }
1463
+
1464
+ // Validate commit hash format (basic sanitization to prevent command injection)
1465
+ if (!/^[a-f0-9]{7,40}$/i.test(beforeCommit)) {
1466
+ console.log('❌ [revert_job] Error: invalid commit hash format');
1467
+ return res.status(400).json({ error: 'Invalid commit hash format' });
1468
+ }
1469
+
1470
+ try {
1471
+ console.log('🔵 [revert_job] Resetting to commit:', beforeCommit);
1472
+ execSync(\`git reset --hard \${beforeCommit}\`, { cwd: ROOT });
1473
+ console.log('✅ [revert_job] Successfully reverted to commit:', beforeCommit);
1474
+ res.json({ success: true });
1475
+ } catch (e) {
1476
+ console.log('❌ [revert_job] Error:', e.message);
1477
+ res.status(500).json({ error: e.message });
1478
+ }
1479
+ });
1480
+
1388
1481
  // Shutdown endpoint to kill the server
1389
1482
  app.post('/shutdown', (req, res) => {
1390
1483
  console.log('🛑 Shutdown endpoint called - terminating server...');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "devbonzai",
3
- "version": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "Quickly set up a local file server in any repository for browser-based file access",
5
5
  "main": "cli.js",
6
6
  "bin": {