amai 0.0.2 → 0.0.4
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/dist/cli.cjs +136 -3
- package/dist/cli.js +136 -3
- package/dist/lib/daemon-entry.cjs +135 -2
- package/dist/lib/daemon-entry.js +135 -2
- package/dist/server.cjs +136 -2
- package/dist/server.d.cts +3 -1
- package/dist/server.d.ts +3 -1
- package/dist/server.js +137 -4
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var pc4 = require('picocolors');
|
|
5
5
|
var WebSocket = require('ws');
|
|
6
|
+
var events = require('events');
|
|
6
7
|
var zod = require('zod');
|
|
7
8
|
var promises = require('fs/promises');
|
|
8
9
|
var path9 = require('path');
|
|
@@ -1261,10 +1262,50 @@ var startHttpServer = (connection) => {
|
|
|
1261
1262
|
}
|
|
1262
1263
|
const app = new hono.Hono();
|
|
1263
1264
|
app.use(cors.cors());
|
|
1264
|
-
app.post("/daemon/status
|
|
1265
|
+
app.post("/daemon/status", (c) => {
|
|
1265
1266
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1266
1267
|
return c.json({ connected: status === "open" });
|
|
1267
1268
|
});
|
|
1269
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1270
|
+
const encoder = new TextEncoder();
|
|
1271
|
+
const stream = new ReadableStream({
|
|
1272
|
+
start(controller) {
|
|
1273
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
const statusHandler = (data) => {
|
|
1278
|
+
try {
|
|
1279
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1280
|
+
|
|
1281
|
+
`));
|
|
1282
|
+
} catch {
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
statusEmitter.on("status", statusHandler);
|
|
1286
|
+
const heartbeatInterval = setInterval(() => {
|
|
1287
|
+
try {
|
|
1288
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1289
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1290
|
+
|
|
1291
|
+
`));
|
|
1292
|
+
} catch {
|
|
1293
|
+
}
|
|
1294
|
+
}, 15e3);
|
|
1295
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1296
|
+
statusEmitter.off("status", statusHandler);
|
|
1297
|
+
clearInterval(heartbeatInterval);
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
return new Response(stream, {
|
|
1302
|
+
headers: {
|
|
1303
|
+
"Content-Type": "text/event-stream",
|
|
1304
|
+
"Cache-Control": "no-cache",
|
|
1305
|
+
"Connection": "keep-alive"
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
});
|
|
1268
1309
|
app.get("context", async (c) => {
|
|
1269
1310
|
const context = getContext(process.cwd());
|
|
1270
1311
|
return c.body(JSON.stringify(context));
|
|
@@ -1506,8 +1547,96 @@ async function login() {
|
|
|
1506
1547
|
throw error;
|
|
1507
1548
|
}
|
|
1508
1549
|
}
|
|
1550
|
+
var ExplanationSchema = zod.z.object({
|
|
1551
|
+
explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1552
|
+
});
|
|
1553
|
+
zod.z.object({
|
|
1554
|
+
command: zod.z.string().describe("The terminal command to execute"),
|
|
1555
|
+
is_background: zod.z.boolean().describe("Whether the command should be run in the background")
|
|
1556
|
+
}).merge(ExplanationSchema);
|
|
1557
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1558
|
+
try {
|
|
1559
|
+
return new Promise((resolve, reject) => {
|
|
1560
|
+
const child = child_process.spawn(command, {
|
|
1561
|
+
cwd: process.cwd(),
|
|
1562
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1563
|
+
shell: true
|
|
1564
|
+
});
|
|
1565
|
+
let stdout = "";
|
|
1566
|
+
let stderr = "";
|
|
1567
|
+
let timeoutId = null;
|
|
1568
|
+
if (timeoutId > 0) {
|
|
1569
|
+
timeoutId = setTimeout(() => {
|
|
1570
|
+
child.kill("SIGKILL");
|
|
1571
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1572
|
+
}, timeout);
|
|
1573
|
+
}
|
|
1574
|
+
child.stdout?.on("data", (data) => {
|
|
1575
|
+
stdout += data.toString();
|
|
1576
|
+
});
|
|
1577
|
+
child.stderr?.on("data", (data) => {
|
|
1578
|
+
stderr += data.toString();
|
|
1579
|
+
});
|
|
1580
|
+
child.stdout.on("close", (code) => {
|
|
1581
|
+
if (timeoutId) {
|
|
1582
|
+
clearTimeout(timeoutId);
|
|
1583
|
+
}
|
|
1584
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1585
|
+
});
|
|
1586
|
+
child.stderr.on("error", (error) => {
|
|
1587
|
+
if (timeoutId) {
|
|
1588
|
+
clearTimeout(timeoutId);
|
|
1589
|
+
}
|
|
1590
|
+
reject(error);
|
|
1591
|
+
});
|
|
1592
|
+
});
|
|
1593
|
+
} catch {
|
|
1594
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1598
|
+
try {
|
|
1599
|
+
if (input?.is_background) {
|
|
1600
|
+
const child = child_process.spawn(input.command, {
|
|
1601
|
+
cwd: projectCwd,
|
|
1602
|
+
detached: true,
|
|
1603
|
+
stdio: "ignore",
|
|
1604
|
+
shell: true
|
|
1605
|
+
});
|
|
1606
|
+
child.unref();
|
|
1607
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1608
|
+
return {
|
|
1609
|
+
success: true,
|
|
1610
|
+
message: `Background command started: ${input.command}`,
|
|
1611
|
+
isBackground: true
|
|
1612
|
+
};
|
|
1613
|
+
} else {
|
|
1614
|
+
const result = await runSecureTerminalCommand(
|
|
1615
|
+
input.command,
|
|
1616
|
+
3e4
|
|
1617
|
+
// 30 second timeout
|
|
1618
|
+
);
|
|
1619
|
+
const success = result?.exitCode === 0;
|
|
1620
|
+
return {
|
|
1621
|
+
success,
|
|
1622
|
+
stdout: result?.stdout?.trim(),
|
|
1623
|
+
stderr: result?.stderr?.trim(),
|
|
1624
|
+
exitCode: result?.exitCode,
|
|
1625
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1626
|
+
};
|
|
1627
|
+
}
|
|
1628
|
+
} catch (error) {
|
|
1629
|
+
console.error("Error while executing the terminal command", error);
|
|
1630
|
+
return {
|
|
1631
|
+
success: false,
|
|
1632
|
+
message: "Error while executing the terminal command",
|
|
1633
|
+
error: error.message
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1509
1637
|
|
|
1510
1638
|
// src/server.ts
|
|
1639
|
+
var statusEmitter = new events.EventEmitter();
|
|
1511
1640
|
var toolExecutors = {
|
|
1512
1641
|
editFile: editFiles,
|
|
1513
1642
|
deleteFile,
|
|
@@ -1515,7 +1644,8 @@ var toolExecutors = {
|
|
|
1515
1644
|
glob: globTool,
|
|
1516
1645
|
listDirectory: list,
|
|
1517
1646
|
readFile: read_file,
|
|
1518
|
-
stringReplace: apply_patch
|
|
1647
|
+
stringReplace: apply_patch,
|
|
1648
|
+
runTerminalCommand
|
|
1519
1649
|
};
|
|
1520
1650
|
function getConnectionStatus(ws) {
|
|
1521
1651
|
return ws.readyState === WebSocket__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket__default.default.OPEN ? "open" : ws.readyState === WebSocket__default.default.CLOSING ? "closing" : "closed";
|
|
@@ -1533,6 +1663,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1533
1663
|
});
|
|
1534
1664
|
ws.on("open", () => {
|
|
1535
1665
|
console.log(pc4__default.default.green("Connected to server agent streams"));
|
|
1666
|
+
statusEmitter.emit("status", { connected: true });
|
|
1536
1667
|
});
|
|
1537
1668
|
ws.on("message", async (data) => {
|
|
1538
1669
|
const message = JSON.parse(data.toString());
|
|
@@ -1562,10 +1693,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1562
1693
|
});
|
|
1563
1694
|
ws.on("close", () => {
|
|
1564
1695
|
console.log(pc4__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1696
|
+
statusEmitter.emit("status", { connected: false });
|
|
1565
1697
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1566
1698
|
});
|
|
1567
1699
|
ws.on("error", (error) => {
|
|
1568
1700
|
console.error(pc4__default.default.red(`WebSocket error: ${error.message}`));
|
|
1701
|
+
statusEmitter.emit("status", { connected: false });
|
|
1569
1702
|
});
|
|
1570
1703
|
return ws;
|
|
1571
1704
|
}
|
|
@@ -1762,7 +1895,7 @@ function getDaemonPid() {
|
|
|
1762
1895
|
return null;
|
|
1763
1896
|
}
|
|
1764
1897
|
}
|
|
1765
|
-
var VERSION = "0.0.
|
|
1898
|
+
var VERSION = "0.0.4";
|
|
1766
1899
|
var PROJECT_DIR = process.cwd();
|
|
1767
1900
|
function promptUser(question) {
|
|
1768
1901
|
const rl = readline__default.default.createInterface({
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import pc4 from 'picocolors';
|
|
3
3
|
import WebSocket from 'ws';
|
|
4
|
+
import { EventEmitter } from 'events';
|
|
4
5
|
import { z } from 'zod';
|
|
5
6
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
6
7
|
import path9, { dirname } from 'path';
|
|
@@ -1249,10 +1250,50 @@ var startHttpServer = (connection) => {
|
|
|
1249
1250
|
}
|
|
1250
1251
|
const app = new Hono();
|
|
1251
1252
|
app.use(cors());
|
|
1252
|
-
app.post("/daemon/status
|
|
1253
|
+
app.post("/daemon/status", (c) => {
|
|
1253
1254
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1254
1255
|
return c.json({ connected: status === "open" });
|
|
1255
1256
|
});
|
|
1257
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1258
|
+
const encoder = new TextEncoder();
|
|
1259
|
+
const stream = new ReadableStream({
|
|
1260
|
+
start(controller) {
|
|
1261
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1262
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1263
|
+
|
|
1264
|
+
`));
|
|
1265
|
+
const statusHandler = (data) => {
|
|
1266
|
+
try {
|
|
1267
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1268
|
+
|
|
1269
|
+
`));
|
|
1270
|
+
} catch {
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
statusEmitter.on("status", statusHandler);
|
|
1274
|
+
const heartbeatInterval = setInterval(() => {
|
|
1275
|
+
try {
|
|
1276
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1277
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1278
|
+
|
|
1279
|
+
`));
|
|
1280
|
+
} catch {
|
|
1281
|
+
}
|
|
1282
|
+
}, 15e3);
|
|
1283
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1284
|
+
statusEmitter.off("status", statusHandler);
|
|
1285
|
+
clearInterval(heartbeatInterval);
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
});
|
|
1289
|
+
return new Response(stream, {
|
|
1290
|
+
headers: {
|
|
1291
|
+
"Content-Type": "text/event-stream",
|
|
1292
|
+
"Cache-Control": "no-cache",
|
|
1293
|
+
"Connection": "keep-alive"
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
});
|
|
1256
1297
|
app.get("context", async (c) => {
|
|
1257
1298
|
const context = getContext(process.cwd());
|
|
1258
1299
|
return c.body(JSON.stringify(context));
|
|
@@ -1494,8 +1535,96 @@ async function login() {
|
|
|
1494
1535
|
throw error;
|
|
1495
1536
|
}
|
|
1496
1537
|
}
|
|
1538
|
+
var ExplanationSchema = z.object({
|
|
1539
|
+
explanation: z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1540
|
+
});
|
|
1541
|
+
z.object({
|
|
1542
|
+
command: z.string().describe("The terminal command to execute"),
|
|
1543
|
+
is_background: z.boolean().describe("Whether the command should be run in the background")
|
|
1544
|
+
}).merge(ExplanationSchema);
|
|
1545
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1546
|
+
try {
|
|
1547
|
+
return new Promise((resolve, reject) => {
|
|
1548
|
+
const child = spawn(command, {
|
|
1549
|
+
cwd: process.cwd(),
|
|
1550
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1551
|
+
shell: true
|
|
1552
|
+
});
|
|
1553
|
+
let stdout = "";
|
|
1554
|
+
let stderr = "";
|
|
1555
|
+
let timeoutId = null;
|
|
1556
|
+
if (timeoutId > 0) {
|
|
1557
|
+
timeoutId = setTimeout(() => {
|
|
1558
|
+
child.kill("SIGKILL");
|
|
1559
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1560
|
+
}, timeout);
|
|
1561
|
+
}
|
|
1562
|
+
child.stdout?.on("data", (data) => {
|
|
1563
|
+
stdout += data.toString();
|
|
1564
|
+
});
|
|
1565
|
+
child.stderr?.on("data", (data) => {
|
|
1566
|
+
stderr += data.toString();
|
|
1567
|
+
});
|
|
1568
|
+
child.stdout.on("close", (code) => {
|
|
1569
|
+
if (timeoutId) {
|
|
1570
|
+
clearTimeout(timeoutId);
|
|
1571
|
+
}
|
|
1572
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1573
|
+
});
|
|
1574
|
+
child.stderr.on("error", (error) => {
|
|
1575
|
+
if (timeoutId) {
|
|
1576
|
+
clearTimeout(timeoutId);
|
|
1577
|
+
}
|
|
1578
|
+
reject(error);
|
|
1579
|
+
});
|
|
1580
|
+
});
|
|
1581
|
+
} catch {
|
|
1582
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1583
|
+
}
|
|
1584
|
+
};
|
|
1585
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1586
|
+
try {
|
|
1587
|
+
if (input?.is_background) {
|
|
1588
|
+
const child = spawn(input.command, {
|
|
1589
|
+
cwd: projectCwd,
|
|
1590
|
+
detached: true,
|
|
1591
|
+
stdio: "ignore",
|
|
1592
|
+
shell: true
|
|
1593
|
+
});
|
|
1594
|
+
child.unref();
|
|
1595
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1596
|
+
return {
|
|
1597
|
+
success: true,
|
|
1598
|
+
message: `Background command started: ${input.command}`,
|
|
1599
|
+
isBackground: true
|
|
1600
|
+
};
|
|
1601
|
+
} else {
|
|
1602
|
+
const result = await runSecureTerminalCommand(
|
|
1603
|
+
input.command,
|
|
1604
|
+
3e4
|
|
1605
|
+
// 30 second timeout
|
|
1606
|
+
);
|
|
1607
|
+
const success = result?.exitCode === 0;
|
|
1608
|
+
return {
|
|
1609
|
+
success,
|
|
1610
|
+
stdout: result?.stdout?.trim(),
|
|
1611
|
+
stderr: result?.stderr?.trim(),
|
|
1612
|
+
exitCode: result?.exitCode,
|
|
1613
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1614
|
+
};
|
|
1615
|
+
}
|
|
1616
|
+
} catch (error) {
|
|
1617
|
+
console.error("Error while executing the terminal command", error);
|
|
1618
|
+
return {
|
|
1619
|
+
success: false,
|
|
1620
|
+
message: "Error while executing the terminal command",
|
|
1621
|
+
error: error.message
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1497
1625
|
|
|
1498
1626
|
// src/server.ts
|
|
1627
|
+
var statusEmitter = new EventEmitter();
|
|
1499
1628
|
var toolExecutors = {
|
|
1500
1629
|
editFile: editFiles,
|
|
1501
1630
|
deleteFile,
|
|
@@ -1503,7 +1632,8 @@ var toolExecutors = {
|
|
|
1503
1632
|
glob: globTool,
|
|
1504
1633
|
listDirectory: list,
|
|
1505
1634
|
readFile: read_file,
|
|
1506
|
-
stringReplace: apply_patch
|
|
1635
|
+
stringReplace: apply_patch,
|
|
1636
|
+
runTerminalCommand
|
|
1507
1637
|
};
|
|
1508
1638
|
function getConnectionStatus(ws) {
|
|
1509
1639
|
return ws.readyState === WebSocket.CONNECTING ? "connecting" : ws.readyState === WebSocket.OPEN ? "open" : ws.readyState === WebSocket.CLOSING ? "closing" : "closed";
|
|
@@ -1521,6 +1651,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1521
1651
|
});
|
|
1522
1652
|
ws.on("open", () => {
|
|
1523
1653
|
console.log(pc4.green("Connected to server agent streams"));
|
|
1654
|
+
statusEmitter.emit("status", { connected: true });
|
|
1524
1655
|
});
|
|
1525
1656
|
ws.on("message", async (data) => {
|
|
1526
1657
|
const message = JSON.parse(data.toString());
|
|
@@ -1550,10 +1681,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1550
1681
|
});
|
|
1551
1682
|
ws.on("close", () => {
|
|
1552
1683
|
console.log(pc4.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1684
|
+
statusEmitter.emit("status", { connected: false });
|
|
1553
1685
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1554
1686
|
});
|
|
1555
1687
|
ws.on("error", (error) => {
|
|
1556
1688
|
console.error(pc4.red(`WebSocket error: ${error.message}`));
|
|
1689
|
+
statusEmitter.emit("status", { connected: false });
|
|
1557
1690
|
});
|
|
1558
1691
|
return ws;
|
|
1559
1692
|
}
|
|
@@ -1750,7 +1883,7 @@ function getDaemonPid() {
|
|
|
1750
1883
|
return null;
|
|
1751
1884
|
}
|
|
1752
1885
|
}
|
|
1753
|
-
var VERSION = "0.0.
|
|
1886
|
+
var VERSION = "0.0.4";
|
|
1754
1887
|
var PROJECT_DIR = process.cwd();
|
|
1755
1888
|
function promptUser(question) {
|
|
1756
1889
|
const rl = readline.createInterface({
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var WebSocket = require('ws');
|
|
5
|
+
var events = require('events');
|
|
5
6
|
var zod = require('zod');
|
|
6
7
|
var promises = require('fs/promises');
|
|
7
8
|
var path9 = require('path');
|
|
@@ -1256,10 +1257,50 @@ var startHttpServer = (connection) => {
|
|
|
1256
1257
|
}
|
|
1257
1258
|
const app = new hono.Hono();
|
|
1258
1259
|
app.use(cors.cors());
|
|
1259
|
-
app.post("/daemon/status
|
|
1260
|
+
app.post("/daemon/status", (c) => {
|
|
1260
1261
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1261
1262
|
return c.json({ connected: status === "open" });
|
|
1262
1263
|
});
|
|
1264
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1265
|
+
const encoder = new TextEncoder();
|
|
1266
|
+
const stream = new ReadableStream({
|
|
1267
|
+
start(controller) {
|
|
1268
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1269
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1270
|
+
|
|
1271
|
+
`));
|
|
1272
|
+
const statusHandler = (data) => {
|
|
1273
|
+
try {
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
statusEmitter.on("status", statusHandler);
|
|
1281
|
+
const heartbeatInterval = setInterval(() => {
|
|
1282
|
+
try {
|
|
1283
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1284
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1285
|
+
|
|
1286
|
+
`));
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
}, 15e3);
|
|
1290
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1291
|
+
statusEmitter.off("status", statusHandler);
|
|
1292
|
+
clearInterval(heartbeatInterval);
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
return new Response(stream, {
|
|
1297
|
+
headers: {
|
|
1298
|
+
"Content-Type": "text/event-stream",
|
|
1299
|
+
"Cache-Control": "no-cache",
|
|
1300
|
+
"Connection": "keep-alive"
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1263
1304
|
app.get("context", async (c) => {
|
|
1264
1305
|
const context = getContext(process.cwd());
|
|
1265
1306
|
return c.body(JSON.stringify(context));
|
|
@@ -1361,8 +1402,96 @@ function getTokens() {
|
|
|
1361
1402
|
const data = JSON.parse(raw);
|
|
1362
1403
|
return data;
|
|
1363
1404
|
}
|
|
1405
|
+
var ExplanationSchema = zod.z.object({
|
|
1406
|
+
explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1407
|
+
});
|
|
1408
|
+
zod.z.object({
|
|
1409
|
+
command: zod.z.string().describe("The terminal command to execute"),
|
|
1410
|
+
is_background: zod.z.boolean().describe("Whether the command should be run in the background")
|
|
1411
|
+
}).merge(ExplanationSchema);
|
|
1412
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1413
|
+
try {
|
|
1414
|
+
return new Promise((resolve, reject) => {
|
|
1415
|
+
const child = child_process.spawn(command, {
|
|
1416
|
+
cwd: process.cwd(),
|
|
1417
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1418
|
+
shell: true
|
|
1419
|
+
});
|
|
1420
|
+
let stdout = "";
|
|
1421
|
+
let stderr = "";
|
|
1422
|
+
let timeoutId = null;
|
|
1423
|
+
if (timeoutId > 0) {
|
|
1424
|
+
timeoutId = setTimeout(() => {
|
|
1425
|
+
child.kill("SIGKILL");
|
|
1426
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1427
|
+
}, timeout);
|
|
1428
|
+
}
|
|
1429
|
+
child.stdout?.on("data", (data) => {
|
|
1430
|
+
stdout += data.toString();
|
|
1431
|
+
});
|
|
1432
|
+
child.stderr?.on("data", (data) => {
|
|
1433
|
+
stderr += data.toString();
|
|
1434
|
+
});
|
|
1435
|
+
child.stdout.on("close", (code) => {
|
|
1436
|
+
if (timeoutId) {
|
|
1437
|
+
clearTimeout(timeoutId);
|
|
1438
|
+
}
|
|
1439
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1440
|
+
});
|
|
1441
|
+
child.stderr.on("error", (error) => {
|
|
1442
|
+
if (timeoutId) {
|
|
1443
|
+
clearTimeout(timeoutId);
|
|
1444
|
+
}
|
|
1445
|
+
reject(error);
|
|
1446
|
+
});
|
|
1447
|
+
});
|
|
1448
|
+
} catch {
|
|
1449
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1453
|
+
try {
|
|
1454
|
+
if (input?.is_background) {
|
|
1455
|
+
const child = child_process.spawn(input.command, {
|
|
1456
|
+
cwd: projectCwd,
|
|
1457
|
+
detached: true,
|
|
1458
|
+
stdio: "ignore",
|
|
1459
|
+
shell: true
|
|
1460
|
+
});
|
|
1461
|
+
child.unref();
|
|
1462
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1463
|
+
return {
|
|
1464
|
+
success: true,
|
|
1465
|
+
message: `Background command started: ${input.command}`,
|
|
1466
|
+
isBackground: true
|
|
1467
|
+
};
|
|
1468
|
+
} else {
|
|
1469
|
+
const result = await runSecureTerminalCommand(
|
|
1470
|
+
input.command,
|
|
1471
|
+
3e4
|
|
1472
|
+
// 30 second timeout
|
|
1473
|
+
);
|
|
1474
|
+
const success = result?.exitCode === 0;
|
|
1475
|
+
return {
|
|
1476
|
+
success,
|
|
1477
|
+
stdout: result?.stdout?.trim(),
|
|
1478
|
+
stderr: result?.stderr?.trim(),
|
|
1479
|
+
exitCode: result?.exitCode,
|
|
1480
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
console.error("Error while executing the terminal command", error);
|
|
1485
|
+
return {
|
|
1486
|
+
success: false,
|
|
1487
|
+
message: "Error while executing the terminal command",
|
|
1488
|
+
error: error.message
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1364
1492
|
|
|
1365
1493
|
// src/server.ts
|
|
1494
|
+
var statusEmitter = new events.EventEmitter();
|
|
1366
1495
|
var toolExecutors = {
|
|
1367
1496
|
editFile: editFiles,
|
|
1368
1497
|
deleteFile,
|
|
@@ -1370,7 +1499,8 @@ var toolExecutors = {
|
|
|
1370
1499
|
glob: globTool,
|
|
1371
1500
|
listDirectory: list,
|
|
1372
1501
|
readFile: read_file,
|
|
1373
|
-
stringReplace: apply_patch
|
|
1502
|
+
stringReplace: apply_patch,
|
|
1503
|
+
runTerminalCommand
|
|
1374
1504
|
};
|
|
1375
1505
|
function getConnectionStatus(ws) {
|
|
1376
1506
|
return ws.readyState === WebSocket__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket__default.default.OPEN ? "open" : ws.readyState === WebSocket__default.default.CLOSING ? "closing" : "closed";
|
|
@@ -1388,6 +1518,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1388
1518
|
});
|
|
1389
1519
|
ws.on("open", () => {
|
|
1390
1520
|
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1521
|
+
statusEmitter.emit("status", { connected: true });
|
|
1391
1522
|
});
|
|
1392
1523
|
ws.on("message", async (data) => {
|
|
1393
1524
|
const message = JSON.parse(data.toString());
|
|
@@ -1417,10 +1548,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1417
1548
|
});
|
|
1418
1549
|
ws.on("close", () => {
|
|
1419
1550
|
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1551
|
+
statusEmitter.emit("status", { connected: false });
|
|
1420
1552
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1421
1553
|
});
|
|
1422
1554
|
ws.on("error", (error) => {
|
|
1423
1555
|
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1556
|
+
statusEmitter.emit("status", { connected: false });
|
|
1424
1557
|
});
|
|
1425
1558
|
return ws;
|
|
1426
1559
|
}
|
package/dist/lib/daemon-entry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
5
6
|
import path9 from 'path';
|
|
@@ -1246,10 +1247,50 @@ var startHttpServer = (connection) => {
|
|
|
1246
1247
|
}
|
|
1247
1248
|
const app = new Hono();
|
|
1248
1249
|
app.use(cors());
|
|
1249
|
-
app.post("/daemon/status
|
|
1250
|
+
app.post("/daemon/status", (c) => {
|
|
1250
1251
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1251
1252
|
return c.json({ connected: status === "open" });
|
|
1252
1253
|
});
|
|
1254
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1255
|
+
const encoder = new TextEncoder();
|
|
1256
|
+
const stream = new ReadableStream({
|
|
1257
|
+
start(controller) {
|
|
1258
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1259
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1260
|
+
|
|
1261
|
+
`));
|
|
1262
|
+
const statusHandler = (data) => {
|
|
1263
|
+
try {
|
|
1264
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1265
|
+
|
|
1266
|
+
`));
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
statusEmitter.on("status", statusHandler);
|
|
1271
|
+
const heartbeatInterval = setInterval(() => {
|
|
1272
|
+
try {
|
|
1273
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}, 15e3);
|
|
1280
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1281
|
+
statusEmitter.off("status", statusHandler);
|
|
1282
|
+
clearInterval(heartbeatInterval);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
return new Response(stream, {
|
|
1287
|
+
headers: {
|
|
1288
|
+
"Content-Type": "text/event-stream",
|
|
1289
|
+
"Cache-Control": "no-cache",
|
|
1290
|
+
"Connection": "keep-alive"
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1253
1294
|
app.get("context", async (c) => {
|
|
1254
1295
|
const context = getContext(process.cwd());
|
|
1255
1296
|
return c.body(JSON.stringify(context));
|
|
@@ -1351,8 +1392,96 @@ function getTokens() {
|
|
|
1351
1392
|
const data = JSON.parse(raw);
|
|
1352
1393
|
return data;
|
|
1353
1394
|
}
|
|
1395
|
+
var ExplanationSchema = z.object({
|
|
1396
|
+
explanation: z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1397
|
+
});
|
|
1398
|
+
z.object({
|
|
1399
|
+
command: z.string().describe("The terminal command to execute"),
|
|
1400
|
+
is_background: z.boolean().describe("Whether the command should be run in the background")
|
|
1401
|
+
}).merge(ExplanationSchema);
|
|
1402
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1403
|
+
try {
|
|
1404
|
+
return new Promise((resolve, reject) => {
|
|
1405
|
+
const child = spawn(command, {
|
|
1406
|
+
cwd: process.cwd(),
|
|
1407
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1408
|
+
shell: true
|
|
1409
|
+
});
|
|
1410
|
+
let stdout = "";
|
|
1411
|
+
let stderr = "";
|
|
1412
|
+
let timeoutId = null;
|
|
1413
|
+
if (timeoutId > 0) {
|
|
1414
|
+
timeoutId = setTimeout(() => {
|
|
1415
|
+
child.kill("SIGKILL");
|
|
1416
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1417
|
+
}, timeout);
|
|
1418
|
+
}
|
|
1419
|
+
child.stdout?.on("data", (data) => {
|
|
1420
|
+
stdout += data.toString();
|
|
1421
|
+
});
|
|
1422
|
+
child.stderr?.on("data", (data) => {
|
|
1423
|
+
stderr += data.toString();
|
|
1424
|
+
});
|
|
1425
|
+
child.stdout.on("close", (code) => {
|
|
1426
|
+
if (timeoutId) {
|
|
1427
|
+
clearTimeout(timeoutId);
|
|
1428
|
+
}
|
|
1429
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1430
|
+
});
|
|
1431
|
+
child.stderr.on("error", (error) => {
|
|
1432
|
+
if (timeoutId) {
|
|
1433
|
+
clearTimeout(timeoutId);
|
|
1434
|
+
}
|
|
1435
|
+
reject(error);
|
|
1436
|
+
});
|
|
1437
|
+
});
|
|
1438
|
+
} catch {
|
|
1439
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1443
|
+
try {
|
|
1444
|
+
if (input?.is_background) {
|
|
1445
|
+
const child = spawn(input.command, {
|
|
1446
|
+
cwd: projectCwd,
|
|
1447
|
+
detached: true,
|
|
1448
|
+
stdio: "ignore",
|
|
1449
|
+
shell: true
|
|
1450
|
+
});
|
|
1451
|
+
child.unref();
|
|
1452
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1453
|
+
return {
|
|
1454
|
+
success: true,
|
|
1455
|
+
message: `Background command started: ${input.command}`,
|
|
1456
|
+
isBackground: true
|
|
1457
|
+
};
|
|
1458
|
+
} else {
|
|
1459
|
+
const result = await runSecureTerminalCommand(
|
|
1460
|
+
input.command,
|
|
1461
|
+
3e4
|
|
1462
|
+
// 30 second timeout
|
|
1463
|
+
);
|
|
1464
|
+
const success = result?.exitCode === 0;
|
|
1465
|
+
return {
|
|
1466
|
+
success,
|
|
1467
|
+
stdout: result?.stdout?.trim(),
|
|
1468
|
+
stderr: result?.stderr?.trim(),
|
|
1469
|
+
exitCode: result?.exitCode,
|
|
1470
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
console.error("Error while executing the terminal command", error);
|
|
1475
|
+
return {
|
|
1476
|
+
success: false,
|
|
1477
|
+
message: "Error while executing the terminal command",
|
|
1478
|
+
error: error.message
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1354
1482
|
|
|
1355
1483
|
// src/server.ts
|
|
1484
|
+
var statusEmitter = new EventEmitter();
|
|
1356
1485
|
var toolExecutors = {
|
|
1357
1486
|
editFile: editFiles,
|
|
1358
1487
|
deleteFile,
|
|
@@ -1360,7 +1489,8 @@ var toolExecutors = {
|
|
|
1360
1489
|
glob: globTool,
|
|
1361
1490
|
listDirectory: list,
|
|
1362
1491
|
readFile: read_file,
|
|
1363
|
-
stringReplace: apply_patch
|
|
1492
|
+
stringReplace: apply_patch,
|
|
1493
|
+
runTerminalCommand
|
|
1364
1494
|
};
|
|
1365
1495
|
function getConnectionStatus(ws) {
|
|
1366
1496
|
return ws.readyState === WebSocket.CONNECTING ? "connecting" : ws.readyState === WebSocket.OPEN ? "open" : ws.readyState === WebSocket.CLOSING ? "closing" : "closed";
|
|
@@ -1378,6 +1508,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1378
1508
|
});
|
|
1379
1509
|
ws.on("open", () => {
|
|
1380
1510
|
console.log(pc2.green("Connected to server agent streams"));
|
|
1511
|
+
statusEmitter.emit("status", { connected: true });
|
|
1381
1512
|
});
|
|
1382
1513
|
ws.on("message", async (data) => {
|
|
1383
1514
|
const message = JSON.parse(data.toString());
|
|
@@ -1407,10 +1538,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1407
1538
|
});
|
|
1408
1539
|
ws.on("close", () => {
|
|
1409
1540
|
console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1541
|
+
statusEmitter.emit("status", { connected: false });
|
|
1410
1542
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1411
1543
|
});
|
|
1412
1544
|
ws.on("error", (error) => {
|
|
1413
1545
|
console.error(pc2.red(`WebSocket error: ${error.message}`));
|
|
1546
|
+
statusEmitter.emit("status", { connected: false });
|
|
1414
1547
|
});
|
|
1415
1548
|
return ws;
|
|
1416
1549
|
}
|
package/dist/server.cjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var WebSocket = require('ws');
|
|
5
|
+
var events = require('events');
|
|
5
6
|
var zod = require('zod');
|
|
6
7
|
var promises = require('fs/promises');
|
|
7
8
|
var path9 = require('path');
|
|
@@ -1256,10 +1257,50 @@ var startHttpServer = (connection) => {
|
|
|
1256
1257
|
}
|
|
1257
1258
|
const app = new hono.Hono();
|
|
1258
1259
|
app.use(cors.cors());
|
|
1259
|
-
app.post("/daemon/status
|
|
1260
|
+
app.post("/daemon/status", (c) => {
|
|
1260
1261
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1261
1262
|
return c.json({ connected: status === "open" });
|
|
1262
1263
|
});
|
|
1264
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1265
|
+
const encoder = new TextEncoder();
|
|
1266
|
+
const stream = new ReadableStream({
|
|
1267
|
+
start(controller) {
|
|
1268
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1269
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1270
|
+
|
|
1271
|
+
`));
|
|
1272
|
+
const statusHandler = (data) => {
|
|
1273
|
+
try {
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
};
|
|
1280
|
+
statusEmitter.on("status", statusHandler);
|
|
1281
|
+
const heartbeatInterval = setInterval(() => {
|
|
1282
|
+
try {
|
|
1283
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1284
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1285
|
+
|
|
1286
|
+
`));
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
}, 15e3);
|
|
1290
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1291
|
+
statusEmitter.off("status", statusHandler);
|
|
1292
|
+
clearInterval(heartbeatInterval);
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
return new Response(stream, {
|
|
1297
|
+
headers: {
|
|
1298
|
+
"Content-Type": "text/event-stream",
|
|
1299
|
+
"Cache-Control": "no-cache",
|
|
1300
|
+
"Connection": "keep-alive"
|
|
1301
|
+
}
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1263
1304
|
app.get("context", async (c) => {
|
|
1264
1305
|
const context = getContext(process.cwd());
|
|
1265
1306
|
return c.body(JSON.stringify(context));
|
|
@@ -1361,8 +1402,96 @@ function getTokens() {
|
|
|
1361
1402
|
const data = JSON.parse(raw);
|
|
1362
1403
|
return data;
|
|
1363
1404
|
}
|
|
1405
|
+
var ExplanationSchema = zod.z.object({
|
|
1406
|
+
explanation: zod.z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1407
|
+
});
|
|
1408
|
+
zod.z.object({
|
|
1409
|
+
command: zod.z.string().describe("The terminal command to execute"),
|
|
1410
|
+
is_background: zod.z.boolean().describe("Whether the command should be run in the background")
|
|
1411
|
+
}).merge(ExplanationSchema);
|
|
1412
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1413
|
+
try {
|
|
1414
|
+
return new Promise((resolve, reject) => {
|
|
1415
|
+
const child = child_process.spawn(command, {
|
|
1416
|
+
cwd: process.cwd(),
|
|
1417
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1418
|
+
shell: true
|
|
1419
|
+
});
|
|
1420
|
+
let stdout = "";
|
|
1421
|
+
let stderr = "";
|
|
1422
|
+
let timeoutId = null;
|
|
1423
|
+
if (timeoutId > 0) {
|
|
1424
|
+
timeoutId = setTimeout(() => {
|
|
1425
|
+
child.kill("SIGKILL");
|
|
1426
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1427
|
+
}, timeout);
|
|
1428
|
+
}
|
|
1429
|
+
child.stdout?.on("data", (data) => {
|
|
1430
|
+
stdout += data.toString();
|
|
1431
|
+
});
|
|
1432
|
+
child.stderr?.on("data", (data) => {
|
|
1433
|
+
stderr += data.toString();
|
|
1434
|
+
});
|
|
1435
|
+
child.stdout.on("close", (code) => {
|
|
1436
|
+
if (timeoutId) {
|
|
1437
|
+
clearTimeout(timeoutId);
|
|
1438
|
+
}
|
|
1439
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1440
|
+
});
|
|
1441
|
+
child.stderr.on("error", (error) => {
|
|
1442
|
+
if (timeoutId) {
|
|
1443
|
+
clearTimeout(timeoutId);
|
|
1444
|
+
}
|
|
1445
|
+
reject(error);
|
|
1446
|
+
});
|
|
1447
|
+
});
|
|
1448
|
+
} catch {
|
|
1449
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1450
|
+
}
|
|
1451
|
+
};
|
|
1452
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1453
|
+
try {
|
|
1454
|
+
if (input?.is_background) {
|
|
1455
|
+
const child = child_process.spawn(input.command, {
|
|
1456
|
+
cwd: projectCwd,
|
|
1457
|
+
detached: true,
|
|
1458
|
+
stdio: "ignore",
|
|
1459
|
+
shell: true
|
|
1460
|
+
});
|
|
1461
|
+
child.unref();
|
|
1462
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1463
|
+
return {
|
|
1464
|
+
success: true,
|
|
1465
|
+
message: `Background command started: ${input.command}`,
|
|
1466
|
+
isBackground: true
|
|
1467
|
+
};
|
|
1468
|
+
} else {
|
|
1469
|
+
const result = await runSecureTerminalCommand(
|
|
1470
|
+
input.command,
|
|
1471
|
+
3e4
|
|
1472
|
+
// 30 second timeout
|
|
1473
|
+
);
|
|
1474
|
+
const success = result?.exitCode === 0;
|
|
1475
|
+
return {
|
|
1476
|
+
success,
|
|
1477
|
+
stdout: result?.stdout?.trim(),
|
|
1478
|
+
stderr: result?.stderr?.trim(),
|
|
1479
|
+
exitCode: result?.exitCode,
|
|
1480
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
console.error("Error while executing the terminal command", error);
|
|
1485
|
+
return {
|
|
1486
|
+
success: false,
|
|
1487
|
+
message: "Error while executing the terminal command",
|
|
1488
|
+
error: error.message
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
};
|
|
1364
1492
|
|
|
1365
1493
|
// src/server.ts
|
|
1494
|
+
var statusEmitter = new events.EventEmitter();
|
|
1366
1495
|
var toolExecutors = {
|
|
1367
1496
|
editFile: editFiles,
|
|
1368
1497
|
deleteFile,
|
|
@@ -1370,7 +1499,8 @@ var toolExecutors = {
|
|
|
1370
1499
|
glob: globTool,
|
|
1371
1500
|
listDirectory: list,
|
|
1372
1501
|
readFile: read_file,
|
|
1373
|
-
stringReplace: apply_patch
|
|
1502
|
+
stringReplace: apply_patch,
|
|
1503
|
+
runTerminalCommand
|
|
1374
1504
|
};
|
|
1375
1505
|
function getConnectionStatus(ws) {
|
|
1376
1506
|
return ws.readyState === WebSocket__default.default.CONNECTING ? "connecting" : ws.readyState === WebSocket__default.default.OPEN ? "open" : ws.readyState === WebSocket__default.default.CLOSING ? "closing" : "closed";
|
|
@@ -1388,6 +1518,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1388
1518
|
});
|
|
1389
1519
|
ws.on("open", () => {
|
|
1390
1520
|
console.log(pc2__default.default.green("Connected to server agent streams"));
|
|
1521
|
+
statusEmitter.emit("status", { connected: true });
|
|
1391
1522
|
});
|
|
1392
1523
|
ws.on("message", async (data) => {
|
|
1393
1524
|
const message = JSON.parse(data.toString());
|
|
@@ -1417,10 +1548,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1417
1548
|
});
|
|
1418
1549
|
ws.on("close", () => {
|
|
1419
1550
|
console.log(pc2__default.default.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1551
|
+
statusEmitter.emit("status", { connected: false });
|
|
1420
1552
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1421
1553
|
});
|
|
1422
1554
|
ws.on("error", (error) => {
|
|
1423
1555
|
console.error(pc2__default.default.red(`WebSocket error: ${error.message}`));
|
|
1556
|
+
statusEmitter.emit("status", { connected: false });
|
|
1424
1557
|
});
|
|
1425
1558
|
return ws;
|
|
1426
1559
|
}
|
|
@@ -1435,3 +1568,4 @@ async function main() {
|
|
|
1435
1568
|
exports.connectToServer = connectToServer2;
|
|
1436
1569
|
exports.getConnectionStatus = getConnectionStatus;
|
|
1437
1570
|
exports.main = main;
|
|
1571
|
+
exports.statusEmitter = statusEmitter;
|
package/dist/server.d.cts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
2
3
|
|
|
4
|
+
declare const statusEmitter: EventEmitter<[never]>;
|
|
3
5
|
declare function getConnectionStatus(ws: WebSocket): 'connecting' | 'open' | 'closing' | 'closed';
|
|
4
6
|
declare function connectToServer(serverUrl?: string): WebSocket;
|
|
5
7
|
declare function main(): Promise<void>;
|
|
6
8
|
|
|
7
|
-
export { connectToServer, getConnectionStatus, main };
|
|
9
|
+
export { connectToServer, getConnectionStatus, main, statusEmitter };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import WebSocket from 'ws';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
2
3
|
|
|
4
|
+
declare const statusEmitter: EventEmitter<[never]>;
|
|
3
5
|
declare function getConnectionStatus(ws: WebSocket): 'connecting' | 'open' | 'closing' | 'closed';
|
|
4
6
|
declare function connectToServer(serverUrl?: string): WebSocket;
|
|
5
7
|
declare function main(): Promise<void>;
|
|
6
8
|
|
|
7
|
-
export { connectToServer, getConnectionStatus, main };
|
|
9
|
+
export { connectToServer, getConnectionStatus, main, statusEmitter };
|
package/dist/server.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { readFile, writeFile, stat, access, readdir, glob, unlink, mkdir } from 'fs/promises';
|
|
5
6
|
import path9 from 'path';
|
|
6
7
|
import fs3, { readdirSync } from 'fs';
|
|
7
8
|
import os2 from 'os';
|
|
8
|
-
import { exec } from 'child_process';
|
|
9
|
+
import { exec, spawn } from 'child_process';
|
|
9
10
|
import { promisify } from 'util';
|
|
10
11
|
import pc2 from 'picocolors';
|
|
11
12
|
import { Hono } from 'hono';
|
|
@@ -1246,10 +1247,50 @@ var startHttpServer = (connection) => {
|
|
|
1246
1247
|
}
|
|
1247
1248
|
const app = new Hono();
|
|
1248
1249
|
app.use(cors());
|
|
1249
|
-
app.post("/daemon/status
|
|
1250
|
+
app.post("/daemon/status", (c) => {
|
|
1250
1251
|
const status = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1251
1252
|
return c.json({ connected: status === "open" });
|
|
1252
1253
|
});
|
|
1254
|
+
app.get("/daemon/status/stream", (c) => {
|
|
1255
|
+
const encoder = new TextEncoder();
|
|
1256
|
+
const stream = new ReadableStream({
|
|
1257
|
+
start(controller) {
|
|
1258
|
+
const initialStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1259
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: initialStatus === "open" })}
|
|
1260
|
+
|
|
1261
|
+
`));
|
|
1262
|
+
const statusHandler = (data) => {
|
|
1263
|
+
try {
|
|
1264
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(data)}
|
|
1265
|
+
|
|
1266
|
+
`));
|
|
1267
|
+
} catch {
|
|
1268
|
+
}
|
|
1269
|
+
};
|
|
1270
|
+
statusEmitter.on("status", statusHandler);
|
|
1271
|
+
const heartbeatInterval = setInterval(() => {
|
|
1272
|
+
try {
|
|
1273
|
+
const currentStatus = wsConnection ? getConnectionStatus(wsConnection) : "closed";
|
|
1274
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify({ connected: currentStatus === "open" })}
|
|
1275
|
+
|
|
1276
|
+
`));
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}, 15e3);
|
|
1280
|
+
c.req.raw.signal.addEventListener("abort", () => {
|
|
1281
|
+
statusEmitter.off("status", statusHandler);
|
|
1282
|
+
clearInterval(heartbeatInterval);
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
return new Response(stream, {
|
|
1287
|
+
headers: {
|
|
1288
|
+
"Content-Type": "text/event-stream",
|
|
1289
|
+
"Cache-Control": "no-cache",
|
|
1290
|
+
"Connection": "keep-alive"
|
|
1291
|
+
}
|
|
1292
|
+
});
|
|
1293
|
+
});
|
|
1253
1294
|
app.get("context", async (c) => {
|
|
1254
1295
|
const context = getContext(process.cwd());
|
|
1255
1296
|
return c.body(JSON.stringify(context));
|
|
@@ -1351,8 +1392,96 @@ function getTokens() {
|
|
|
1351
1392
|
const data = JSON.parse(raw);
|
|
1352
1393
|
return data;
|
|
1353
1394
|
}
|
|
1395
|
+
var ExplanationSchema = z.object({
|
|
1396
|
+
explanation: z.string().describe("One sentence explanation as to why this tool is being used")
|
|
1397
|
+
});
|
|
1398
|
+
z.object({
|
|
1399
|
+
command: z.string().describe("The terminal command to execute"),
|
|
1400
|
+
is_background: z.boolean().describe("Whether the command should be run in the background")
|
|
1401
|
+
}).merge(ExplanationSchema);
|
|
1402
|
+
var runSecureTerminalCommand = async (command, timeout) => {
|
|
1403
|
+
try {
|
|
1404
|
+
return new Promise((resolve, reject) => {
|
|
1405
|
+
const child = spawn(command, {
|
|
1406
|
+
cwd: process.cwd(),
|
|
1407
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1408
|
+
shell: true
|
|
1409
|
+
});
|
|
1410
|
+
let stdout = "";
|
|
1411
|
+
let stderr = "";
|
|
1412
|
+
let timeoutId = null;
|
|
1413
|
+
if (timeoutId > 0) {
|
|
1414
|
+
timeoutId = setTimeout(() => {
|
|
1415
|
+
child.kill("SIGKILL");
|
|
1416
|
+
reject(new Error(`Command timed out after ${timeout}ms`));
|
|
1417
|
+
}, timeout);
|
|
1418
|
+
}
|
|
1419
|
+
child.stdout?.on("data", (data) => {
|
|
1420
|
+
stdout += data.toString();
|
|
1421
|
+
});
|
|
1422
|
+
child.stderr?.on("data", (data) => {
|
|
1423
|
+
stderr += data.toString();
|
|
1424
|
+
});
|
|
1425
|
+
child.stdout.on("close", (code) => {
|
|
1426
|
+
if (timeoutId) {
|
|
1427
|
+
clearTimeout(timeoutId);
|
|
1428
|
+
}
|
|
1429
|
+
resolve({ stdout, stderr, exitCode: code || 0 });
|
|
1430
|
+
});
|
|
1431
|
+
child.stderr.on("error", (error) => {
|
|
1432
|
+
if (timeoutId) {
|
|
1433
|
+
clearTimeout(timeoutId);
|
|
1434
|
+
}
|
|
1435
|
+
reject(error);
|
|
1436
|
+
});
|
|
1437
|
+
});
|
|
1438
|
+
} catch {
|
|
1439
|
+
console.error("Error while ecexuting the securedShell command");
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
var runTerminalCommand = async (input, projectCwd) => {
|
|
1443
|
+
try {
|
|
1444
|
+
if (input?.is_background) {
|
|
1445
|
+
const child = spawn(input.command, {
|
|
1446
|
+
cwd: projectCwd,
|
|
1447
|
+
detached: true,
|
|
1448
|
+
stdio: "ignore",
|
|
1449
|
+
shell: true
|
|
1450
|
+
});
|
|
1451
|
+
child.unref();
|
|
1452
|
+
console.log(`[LOCAL] Background command started: ${input.command}`);
|
|
1453
|
+
return {
|
|
1454
|
+
success: true,
|
|
1455
|
+
message: `Background command started: ${input.command}`,
|
|
1456
|
+
isBackground: true
|
|
1457
|
+
};
|
|
1458
|
+
} else {
|
|
1459
|
+
const result = await runSecureTerminalCommand(
|
|
1460
|
+
input.command,
|
|
1461
|
+
3e4
|
|
1462
|
+
// 30 second timeout
|
|
1463
|
+
);
|
|
1464
|
+
const success = result?.exitCode === 0;
|
|
1465
|
+
return {
|
|
1466
|
+
success,
|
|
1467
|
+
stdout: result?.stdout?.trim(),
|
|
1468
|
+
stderr: result?.stderr?.trim(),
|
|
1469
|
+
exitCode: result?.exitCode,
|
|
1470
|
+
message: success ? `Command executed successfully: ${input.command}` : `Command failed with exit code ${result?.exitCode}: ${input.command}`
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
} catch (error) {
|
|
1474
|
+
console.error("Error while executing the terminal command", error);
|
|
1475
|
+
return {
|
|
1476
|
+
success: false,
|
|
1477
|
+
message: "Error while executing the terminal command",
|
|
1478
|
+
error: error.message
|
|
1479
|
+
};
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1354
1482
|
|
|
1355
1483
|
// src/server.ts
|
|
1484
|
+
var statusEmitter = new EventEmitter();
|
|
1356
1485
|
var toolExecutors = {
|
|
1357
1486
|
editFile: editFiles,
|
|
1358
1487
|
deleteFile,
|
|
@@ -1360,7 +1489,8 @@ var toolExecutors = {
|
|
|
1360
1489
|
glob: globTool,
|
|
1361
1490
|
listDirectory: list,
|
|
1362
1491
|
readFile: read_file,
|
|
1363
|
-
stringReplace: apply_patch
|
|
1492
|
+
stringReplace: apply_patch,
|
|
1493
|
+
runTerminalCommand
|
|
1364
1494
|
};
|
|
1365
1495
|
function getConnectionStatus(ws) {
|
|
1366
1496
|
return ws.readyState === WebSocket.CONNECTING ? "connecting" : ws.readyState === WebSocket.OPEN ? "open" : ws.readyState === WebSocket.CLOSING ? "closing" : "closed";
|
|
@@ -1378,6 +1508,7 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1378
1508
|
});
|
|
1379
1509
|
ws.on("open", () => {
|
|
1380
1510
|
console.log(pc2.green("Connected to server agent streams"));
|
|
1511
|
+
statusEmitter.emit("status", { connected: true });
|
|
1381
1512
|
});
|
|
1382
1513
|
ws.on("message", async (data) => {
|
|
1383
1514
|
const message = JSON.parse(data.toString());
|
|
@@ -1407,10 +1538,12 @@ function connectToServer2(serverUrl = DEFAULT_SERVER_URL) {
|
|
|
1407
1538
|
});
|
|
1408
1539
|
ws.on("close", () => {
|
|
1409
1540
|
console.log(pc2.red("Disconnected from server. Reconnecting in 5s..."));
|
|
1541
|
+
statusEmitter.emit("status", { connected: false });
|
|
1410
1542
|
setTimeout(() => connectToServer2(serverUrl), 5e3);
|
|
1411
1543
|
});
|
|
1412
1544
|
ws.on("error", (error) => {
|
|
1413
1545
|
console.error(pc2.red(`WebSocket error: ${error.message}`));
|
|
1546
|
+
statusEmitter.emit("status", { connected: false });
|
|
1414
1547
|
});
|
|
1415
1548
|
return ws;
|
|
1416
1549
|
}
|
|
@@ -1422,4 +1555,4 @@ async function main() {
|
|
|
1422
1555
|
startHttpServer(connection);
|
|
1423
1556
|
}
|
|
1424
1557
|
|
|
1425
|
-
export { connectToServer2 as connectToServer, getConnectionStatus, main };
|
|
1558
|
+
export { connectToServer2 as connectToServer, getConnectionStatus, main, statusEmitter };
|