devbonzai 2.0.8 → 2.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/cli.js +174 -0
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -93,6 +93,7 @@ app.get('/', (req, res) => {
|
|
|
93
93
|
'POST /open-cursor': 'Open Cursor (body: {path, line?})',
|
|
94
94
|
'POST /analyze_prompt': 'Analyze what files would be modified (body: {prompt})',
|
|
95
95
|
'POST /prompt_agent': 'Execute cursor-agent command (body: {prompt})',
|
|
96
|
+
'POST /prompt_agent_stream': 'Execute cursor-agent with SSE streaming (body: {prompt})',
|
|
96
97
|
'POST /revert_job': 'Revert to a previous commit (body: {beforeCommit})',
|
|
97
98
|
'POST /shutdown': 'Gracefully shutdown the server'
|
|
98
99
|
},
|
|
@@ -1566,6 +1567,179 @@ app.post('/prompt_agent', (req, res) => {
|
|
|
1566
1567
|
});
|
|
1567
1568
|
});
|
|
1568
1569
|
|
|
1570
|
+
// Streaming version of prompt_agent using Server-Sent Events
|
|
1571
|
+
app.post('/prompt_agent_stream', (req, res) => {
|
|
1572
|
+
console.log('🔵 [prompt_agent_stream] Endpoint hit');
|
|
1573
|
+
const { prompt } = req.body;
|
|
1574
|
+
console.log('🔵 [prompt_agent_stream] Received prompt:', prompt ? \`\${prompt.substring(0, 50)}...\` : 'none');
|
|
1575
|
+
|
|
1576
|
+
if (!prompt || typeof prompt !== 'string') {
|
|
1577
|
+
console.log('❌ [prompt_agent_stream] Error: prompt required');
|
|
1578
|
+
return res.status(400).json({ error: 'prompt required' });
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// Set up SSE headers
|
|
1582
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
1583
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
1584
|
+
res.setHeader('Connection', 'keep-alive');
|
|
1585
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1586
|
+
res.flushHeaders();
|
|
1587
|
+
|
|
1588
|
+
// Helper to send SSE events
|
|
1589
|
+
const sendEvent = (type, data) => {
|
|
1590
|
+
res.write(\`data: \${JSON.stringify({ type, ...data })}\\n\\n\`);
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
// Capture beforeCommit
|
|
1594
|
+
let beforeCommit = '';
|
|
1595
|
+
try {
|
|
1596
|
+
beforeCommit = execSync('git rev-parse HEAD', { cwd: ROOT }).toString().trim();
|
|
1597
|
+
console.log('🔵 [prompt_agent_stream] beforeCommit:', beforeCommit);
|
|
1598
|
+
} catch (e) {
|
|
1599
|
+
console.log('⚠️ [prompt_agent_stream] Could not get beforeCommit:', e.message);
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
// Capture initial state of modified files
|
|
1603
|
+
const initiallyModifiedFiles = new Set();
|
|
1604
|
+
try {
|
|
1605
|
+
const initialStatus = execSync('git status --short', { cwd: ROOT }).toString();
|
|
1606
|
+
initialStatus.split('\\n').filter(Boolean).forEach(line => {
|
|
1607
|
+
const filePath = line.substring(3).trim();
|
|
1608
|
+
if (filePath) initiallyModifiedFiles.add(filePath);
|
|
1609
|
+
});
|
|
1610
|
+
} catch (e) {
|
|
1611
|
+
// Ignore
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// Send starting event
|
|
1615
|
+
sendEvent('start', { beforeCommit });
|
|
1616
|
+
|
|
1617
|
+
// Set up file change tracking with real-time updates
|
|
1618
|
+
const changedFiles = new Set();
|
|
1619
|
+
const pollInterval = setInterval(() => {
|
|
1620
|
+
try {
|
|
1621
|
+
const status = execSync('git status --short', { cwd: ROOT }).toString();
|
|
1622
|
+
status.split('\\n').filter(Boolean).forEach(line => {
|
|
1623
|
+
const filePath = line.substring(3).trim();
|
|
1624
|
+
if (filePath && !initiallyModifiedFiles.has(filePath)) {
|
|
1625
|
+
if (!changedFiles.has(filePath)) {
|
|
1626
|
+
changedFiles.add(filePath);
|
|
1627
|
+
console.log('📁 [prompt_agent_stream] File changed:', filePath);
|
|
1628
|
+
// Send real-time update to client
|
|
1629
|
+
sendEvent('file_changed', { path: filePath });
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
});
|
|
1633
|
+
} catch (e) {
|
|
1634
|
+
// Ignore git status errors
|
|
1635
|
+
}
|
|
1636
|
+
}, 500);
|
|
1637
|
+
|
|
1638
|
+
const timeoutMs = parseInt(req.body.timeout) || 5 * 60 * 1000;
|
|
1639
|
+
let timeoutId = null;
|
|
1640
|
+
let responseSent = false;
|
|
1641
|
+
|
|
1642
|
+
const args = ['--print', '--force', '--workspace', '.', prompt];
|
|
1643
|
+
|
|
1644
|
+
console.log('🔵 [prompt_agent_stream] Spawning cursor-agent process...');
|
|
1645
|
+
const proc = spawn(
|
|
1646
|
+
'cursor-agent',
|
|
1647
|
+
args,
|
|
1648
|
+
{
|
|
1649
|
+
cwd: ROOT,
|
|
1650
|
+
env: process.env,
|
|
1651
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
1652
|
+
}
|
|
1653
|
+
);
|
|
1654
|
+
|
|
1655
|
+
console.log('🔵 [prompt_agent_stream] Process spawned, PID:', proc.pid);
|
|
1656
|
+
|
|
1657
|
+
let stdout = '';
|
|
1658
|
+
let stderr = '';
|
|
1659
|
+
|
|
1660
|
+
timeoutId = setTimeout(() => {
|
|
1661
|
+
if (!responseSent && proc && !proc.killed) {
|
|
1662
|
+
console.log('⏱️ [prompt_agent_stream] Timeout reached');
|
|
1663
|
+
clearInterval(pollInterval);
|
|
1664
|
+
proc.kill('SIGTERM');
|
|
1665
|
+
|
|
1666
|
+
setTimeout(() => {
|
|
1667
|
+
if (!proc.killed) proc.kill('SIGKILL');
|
|
1668
|
+
}, 5000);
|
|
1669
|
+
|
|
1670
|
+
if (!responseSent) {
|
|
1671
|
+
responseSent = true;
|
|
1672
|
+
sendEvent('error', {
|
|
1673
|
+
error: 'Process timeout',
|
|
1674
|
+
message: \`cursor-agent exceeded timeout of \${timeoutMs / 1000} seconds\`
|
|
1675
|
+
});
|
|
1676
|
+
sendEvent('complete', {
|
|
1677
|
+
code: -1,
|
|
1678
|
+
stdout,
|
|
1679
|
+
stderr,
|
|
1680
|
+
changedFiles: Array.from(changedFiles),
|
|
1681
|
+
beforeCommit,
|
|
1682
|
+
afterCommit: ''
|
|
1683
|
+
});
|
|
1684
|
+
res.end();
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
}, timeoutMs);
|
|
1688
|
+
|
|
1689
|
+
proc.stdout.on('data', (d) => {
|
|
1690
|
+
stdout += d.toString();
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
proc.stderr.on('data', (d) => {
|
|
1694
|
+
stderr += d.toString();
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1697
|
+
proc.on('error', (error) => {
|
|
1698
|
+
console.log('❌ [prompt_agent_stream] Process error:', error.message);
|
|
1699
|
+
clearInterval(pollInterval);
|
|
1700
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1701
|
+
if (!responseSent) {
|
|
1702
|
+
responseSent = true;
|
|
1703
|
+
sendEvent('error', { error: error.message });
|
|
1704
|
+
res.end();
|
|
1705
|
+
}
|
|
1706
|
+
});
|
|
1707
|
+
|
|
1708
|
+
proc.on('close', (code, signal) => {
|
|
1709
|
+
console.log('🔵 [prompt_agent_stream] Process closed with code:', code);
|
|
1710
|
+
clearInterval(pollInterval);
|
|
1711
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1712
|
+
|
|
1713
|
+
let afterCommit = '';
|
|
1714
|
+
try {
|
|
1715
|
+
afterCommit = execSync('git rev-parse HEAD', { cwd: ROOT }).toString().trim();
|
|
1716
|
+
} catch (e) {
|
|
1717
|
+
// Ignore
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
if (!responseSent) {
|
|
1721
|
+
responseSent = true;
|
|
1722
|
+
sendEvent('complete', {
|
|
1723
|
+
code,
|
|
1724
|
+
stdout,
|
|
1725
|
+
stderr,
|
|
1726
|
+
changedFiles: Array.from(changedFiles),
|
|
1727
|
+
beforeCommit,
|
|
1728
|
+
afterCommit
|
|
1729
|
+
});
|
|
1730
|
+
res.end();
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
// Handle client disconnect - DON'T kill the process, let it complete
|
|
1735
|
+
req.on('close', () => {
|
|
1736
|
+
console.log('🔵 [prompt_agent_stream] Client disconnected (process continues in background)');
|
|
1737
|
+
// Don't kill the process - let it complete
|
|
1738
|
+
// Just mark that we shouldn't try to send more events
|
|
1739
|
+
responseSent = true;
|
|
1740
|
+
});
|
|
1741
|
+
});
|
|
1742
|
+
|
|
1569
1743
|
// Revert job endpoint to reset to a previous commit
|
|
1570
1744
|
app.post('/revert_job', (req, res) => {
|
|
1571
1745
|
console.log('🔵 [revert_job] Endpoint hit');
|