@nomad-e/bluma-cli 0.0.106 → 0.0.108
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/config/native_tools.json +7 -42
- package/dist/main.js +211 -304
- package/package.json +1 -1
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"nativeTools": [
|
|
3
|
-
{
|
|
4
|
-
"type": "function",
|
|
5
|
-
"function": {
|
|
6
|
-
"name": "shell_command",
|
|
7
|
-
"description": "Executes terminal commands in a universal and robust way. Automatically detects the appropriate shell (bash/sh on Linux/macOS, cmd/PowerShell on Windows). Handles timeouts gracefully and captures all output. Use for: installing packages (npm/pip/cargo), running tests, building projects, git operations, and any shell commands.",
|
|
8
|
-
"parameters": {
|
|
9
|
-
"type": "object",
|
|
10
|
-
"properties": {
|
|
11
|
-
"command": {
|
|
12
|
-
"type": "string",
|
|
13
|
-
"description": "The shell command to execute. Examples: 'npm install express', 'git status', 'pytest tests/', 'cargo build --release'. The command will be executed in the appropriate shell for the OS."
|
|
14
|
-
},
|
|
15
|
-
"timeout": {
|
|
16
|
-
"type": "integer",
|
|
17
|
-
"description": "Maximum execution time in seconds. Default is 300 (5 minutes). Increase for long-running commands like large builds or npm installs. The process will be terminated gracefully if timeout is exceeded.",
|
|
18
|
-
"default": 300,
|
|
19
|
-
"minimum": 1,
|
|
20
|
-
"maximum": 3600
|
|
21
|
-
},
|
|
22
|
-
"cwd": {
|
|
23
|
-
"type": "string",
|
|
24
|
-
"description": "Working directory for command execution. Must be an absolute path. Defaults to current working directory if not specified. Use this to execute commands in specific project folders."
|
|
25
|
-
},
|
|
26
|
-
"verbose": {
|
|
27
|
-
"type": "boolean",
|
|
28
|
-
"description": "If true, returns detailed execution report including platform info, duration, and full output. If false, returns concise output with just status, exit code, and stdout/stderr. Use verbose=true for debugging command issues.",
|
|
29
|
-
"default": false
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"required": [
|
|
33
|
-
"command"
|
|
34
|
-
]
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
3
|
{
|
|
39
4
|
"type": "function",
|
|
40
5
|
"function": {
|
|
@@ -348,22 +313,22 @@
|
|
|
348
313
|
{
|
|
349
314
|
"type": "function",
|
|
350
315
|
"function": {
|
|
351
|
-
"name": "
|
|
352
|
-
"description": "
|
|
316
|
+
"name": "shell_command",
|
|
317
|
+
"description": "Execute a shell command. The command runs in background and returns a command_id. Use command_status to check progress and get output. Security: blocks sudo, rm -rf, fork bombs, and other dangerous commands. Output is limited to 30KB/200 lines to avoid context overflow.",
|
|
353
318
|
"parameters": {
|
|
354
319
|
"type": "object",
|
|
355
320
|
"properties": {
|
|
356
321
|
"command": {
|
|
357
322
|
"type": "string",
|
|
358
|
-
"description": "The shell command to execute
|
|
323
|
+
"description": "The shell command to execute. Examples: 'npm install', 'git status', 'docker build', 'pytest'. Cross-platform (bash/sh on Unix, cmd on Windows)."
|
|
359
324
|
},
|
|
360
325
|
"cwd": {
|
|
361
326
|
"type": "string",
|
|
362
|
-
"description": "Working directory
|
|
327
|
+
"description": "Working directory. Defaults to current directory."
|
|
363
328
|
},
|
|
364
329
|
"timeout": {
|
|
365
330
|
"type": "integer",
|
|
366
|
-
"description": "Timeout in seconds. 0
|
|
331
|
+
"description": "Timeout in seconds. 0 = no timeout. Default is 0.",
|
|
367
332
|
"default": 0
|
|
368
333
|
}
|
|
369
334
|
},
|
|
@@ -377,13 +342,13 @@
|
|
|
377
342
|
"type": "function",
|
|
378
343
|
"function": {
|
|
379
344
|
"name": "command_status",
|
|
380
|
-
"description": "Check the status of a
|
|
345
|
+
"description": "Check the status of a command started with shell_command. Returns status, output, and exit code.",
|
|
381
346
|
"parameters": {
|
|
382
347
|
"type": "object",
|
|
383
348
|
"properties": {
|
|
384
349
|
"command_id": {
|
|
385
350
|
"type": "string",
|
|
386
|
-
"description": "The command ID returned by
|
|
351
|
+
"description": "The command ID returned by shell_command."
|
|
387
352
|
},
|
|
388
353
|
"wait_seconds": {
|
|
389
354
|
"type": "integer",
|
package/dist/main.js
CHANGED
|
@@ -1316,255 +1316,16 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
1316
1316
|
import OpenAI from "openai";
|
|
1317
1317
|
import * as dotenv from "dotenv";
|
|
1318
1318
|
import path15 from "path";
|
|
1319
|
-
import
|
|
1319
|
+
import os8 from "os";
|
|
1320
1320
|
|
|
1321
1321
|
// src/app/agent/tool_invoker.ts
|
|
1322
1322
|
import { promises as fs8 } from "fs";
|
|
1323
1323
|
import path10 from "path";
|
|
1324
1324
|
import { fileURLToPath } from "url";
|
|
1325
1325
|
|
|
1326
|
-
// src/app/agent/tools/natives/shell_command.ts
|
|
1327
|
-
import os from "os";
|
|
1328
|
-
import { spawn } from "child_process";
|
|
1329
|
-
var MAX_OUTPUT_SIZE = 1e5;
|
|
1330
|
-
var OUTPUT_TRUNCATION_MESSAGE = "\n\n[OUTPUT TRUNCATED - exceeded 100KB limit. Use pagination or redirect to file for full output.]";
|
|
1331
|
-
var DANGEROUS_PATTERNS = [
|
|
1332
|
-
// === ELEVAÇÃO DE PRIVILÉGIOS ===
|
|
1333
|
-
/^sudo\s+/i,
|
|
1334
|
-
// sudo commands
|
|
1335
|
-
/^doas\s+/i,
|
|
1336
|
-
// doas (BSD sudo alternative)
|
|
1337
|
-
/^su\s+/i,
|
|
1338
|
-
// switch user
|
|
1339
|
-
/^pkexec\s+/i,
|
|
1340
|
-
// PolicyKit execution
|
|
1341
|
-
/\|\s*sudo\s+/i,
|
|
1342
|
-
// piped to sudo
|
|
1343
|
-
/;\s*sudo\s+/i,
|
|
1344
|
-
// chained with sudo
|
|
1345
|
-
/&&\s*sudo\s+/i,
|
|
1346
|
-
// AND chained with sudo
|
|
1347
|
-
// === COMANDOS DESTRUTIVOS ===
|
|
1348
|
-
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
1349
|
-
// rm com paths perigosos (/, ~)
|
|
1350
|
-
/\brm\s+-[rf]*\s+\*/i,
|
|
1351
|
-
// rm -rf *
|
|
1352
|
-
/\bchmod\s+(777|666)\s+\//i,
|
|
1353
|
-
// chmod 777/666 em paths root
|
|
1354
|
-
/\bchown\s+.*\s+\//i,
|
|
1355
|
-
// chown em paths root
|
|
1356
|
-
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
1357
|
-
// dd para discos
|
|
1358
|
-
/\bmkfs\./i,
|
|
1359
|
-
// format filesystem
|
|
1360
|
-
/>\s*\/dev\/(sd|hd|nvme)/i,
|
|
1361
|
-
// redirect para disco
|
|
1362
|
-
/\bshred\s+/i,
|
|
1363
|
-
// secure delete
|
|
1364
|
-
// === FORK BOMB / RESOURCE EXHAUSTION ===
|
|
1365
|
-
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
1366
|
-
// fork bomb clássico
|
|
1367
|
-
/\bwhile\s+true\s*;\s*do/i,
|
|
1368
|
-
// infinite loop
|
|
1369
|
-
/\byes\s+\|/i,
|
|
1370
|
-
// yes piped (resource exhaustion)
|
|
1371
|
-
// === NETWORK PERIGOSO ===
|
|
1372
|
-
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
1373
|
-
// curl | bash (remote code exec)
|
|
1374
|
-
/\bwget\s+.*\|\s*(ba)?sh/i,
|
|
1375
|
-
// wget | bash
|
|
1376
|
-
/\bnc\s+-[el]/i
|
|
1377
|
-
// netcat listener (backdoor)
|
|
1378
|
-
];
|
|
1379
|
-
var INTERACTIVE_PATTERNS = [
|
|
1380
|
-
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
1381
|
-
// editors
|
|
1382
|
-
/^(less|more|most)\s*/i,
|
|
1383
|
-
// pagers
|
|
1384
|
-
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
1385
|
-
// monitoring tools
|
|
1386
|
-
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
1387
|
-
// remote connections
|
|
1388
|
-
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
1389
|
-
// database CLIs (sem script)
|
|
1390
|
-
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
1391
|
-
// REPLs sem script
|
|
1392
|
-
/^(gdb|lldb)\s*/i,
|
|
1393
|
-
// debuggers
|
|
1394
|
-
/^(bc|dc)\s*$/i
|
|
1395
|
-
// calculators
|
|
1396
|
-
];
|
|
1397
|
-
function shellCommand(args) {
|
|
1398
|
-
const {
|
|
1399
|
-
command,
|
|
1400
|
-
timeout = 300,
|
|
1401
|
-
// 5 minutos por padrão
|
|
1402
|
-
cwd = process.cwd(),
|
|
1403
|
-
verbose = false
|
|
1404
|
-
} = args;
|
|
1405
|
-
return new Promise((resolve) => {
|
|
1406
|
-
const startTime = Date.now();
|
|
1407
|
-
const platform = os.platform();
|
|
1408
|
-
const commandTrimmed = command.trim();
|
|
1409
|
-
for (const pattern of DANGEROUS_PATTERNS) {
|
|
1410
|
-
if (pattern.test(commandTrimmed)) {
|
|
1411
|
-
const result = {
|
|
1412
|
-
status: "blocked",
|
|
1413
|
-
exitCode: null,
|
|
1414
|
-
stdout: "",
|
|
1415
|
-
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
1416
|
-
1. Run the command manually in terminal
|
|
1417
|
-
2. Configure passwordless sudo for this specific command
|
|
1418
|
-
3. Run BluMa as root (not recommended)`,
|
|
1419
|
-
command,
|
|
1420
|
-
cwd,
|
|
1421
|
-
platform,
|
|
1422
|
-
duration: Date.now() - startTime,
|
|
1423
|
-
blockedReason: "requires_sudo"
|
|
1424
|
-
};
|
|
1425
|
-
return resolve(formatResult(result, verbose));
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
1429
|
-
if (pattern.test(commandTrimmed)) {
|
|
1430
|
-
const result = {
|
|
1431
|
-
status: "blocked",
|
|
1432
|
-
exitCode: null,
|
|
1433
|
-
stdout: "",
|
|
1434
|
-
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
1435
|
-
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
1436
|
-
2. Run the command manually in terminal
|
|
1437
|
-
3. For editors, use edit_tool instead`,
|
|
1438
|
-
command,
|
|
1439
|
-
cwd,
|
|
1440
|
-
platform,
|
|
1441
|
-
duration: Date.now() - startTime,
|
|
1442
|
-
blockedReason: "interactive_command"
|
|
1443
|
-
};
|
|
1444
|
-
return resolve(formatResult(result, verbose));
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
let shellCmd;
|
|
1448
|
-
let shellArgs;
|
|
1449
|
-
if (platform === "win32") {
|
|
1450
|
-
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
1451
|
-
shellArgs = ["/c", command];
|
|
1452
|
-
} else {
|
|
1453
|
-
shellCmd = process.env.SHELL || "/bin/bash";
|
|
1454
|
-
shellArgs = ["-c", command];
|
|
1455
|
-
}
|
|
1456
|
-
let stdout = "";
|
|
1457
|
-
let stderr = "";
|
|
1458
|
-
let stdoutTruncated = false;
|
|
1459
|
-
let stderrTruncated = false;
|
|
1460
|
-
let timedOut = false;
|
|
1461
|
-
let finished = false;
|
|
1462
|
-
const childProcess = spawn(shellCmd, shellArgs, {
|
|
1463
|
-
cwd,
|
|
1464
|
-
env: process.env,
|
|
1465
|
-
// Importante: no Windows, precisamos do shell, mas spawn já lida com isso
|
|
1466
|
-
windowsHide: true
|
|
1467
|
-
});
|
|
1468
|
-
const timeoutId = setTimeout(() => {
|
|
1469
|
-
if (!finished) {
|
|
1470
|
-
timedOut = true;
|
|
1471
|
-
childProcess.kill("SIGTERM");
|
|
1472
|
-
setTimeout(() => {
|
|
1473
|
-
if (!finished) {
|
|
1474
|
-
childProcess.kill("SIGKILL");
|
|
1475
|
-
}
|
|
1476
|
-
}, 2e3);
|
|
1477
|
-
}
|
|
1478
|
-
}, timeout * 1e3);
|
|
1479
|
-
if (childProcess.stdout) {
|
|
1480
|
-
childProcess.stdout.on("data", (data) => {
|
|
1481
|
-
if (!stdoutTruncated) {
|
|
1482
|
-
stdout += data.toString();
|
|
1483
|
-
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
1484
|
-
stdout = stdout.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1485
|
-
stdoutTruncated = true;
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
});
|
|
1489
|
-
}
|
|
1490
|
-
if (childProcess.stderr) {
|
|
1491
|
-
childProcess.stderr.on("data", (data) => {
|
|
1492
|
-
if (!stderrTruncated) {
|
|
1493
|
-
stderr += data.toString();
|
|
1494
|
-
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
1495
|
-
stderr = stderr.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1496
|
-
stderrTruncated = true;
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
|
-
});
|
|
1500
|
-
}
|
|
1501
|
-
childProcess.on("error", (error) => {
|
|
1502
|
-
if (!finished) {
|
|
1503
|
-
finished = true;
|
|
1504
|
-
clearTimeout(timeoutId);
|
|
1505
|
-
const result = {
|
|
1506
|
-
status: "error",
|
|
1507
|
-
exitCode: null,
|
|
1508
|
-
stdout: stdout.trim(),
|
|
1509
|
-
stderr: `Failed to execute command: ${error.message}`,
|
|
1510
|
-
command,
|
|
1511
|
-
cwd,
|
|
1512
|
-
platform,
|
|
1513
|
-
duration: Date.now() - startTime
|
|
1514
|
-
};
|
|
1515
|
-
resolve(formatResult(result, verbose));
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
childProcess.on("close", (code, signal) => {
|
|
1519
|
-
if (!finished) {
|
|
1520
|
-
finished = true;
|
|
1521
|
-
clearTimeout(timeoutId);
|
|
1522
|
-
const result = {
|
|
1523
|
-
status: timedOut ? "timeout" : code === 0 ? "success" : "error",
|
|
1524
|
-
exitCode: code,
|
|
1525
|
-
stdout: stdout.trim(),
|
|
1526
|
-
stderr: timedOut ? `Command timed out after ${timeout} seconds
|
|
1527
|
-
${stderr.trim()}` : stderr.trim(),
|
|
1528
|
-
command,
|
|
1529
|
-
cwd,
|
|
1530
|
-
platform,
|
|
1531
|
-
duration: Date.now() - startTime,
|
|
1532
|
-
truncated: stdoutTruncated || stderrTruncated
|
|
1533
|
-
};
|
|
1534
|
-
resolve(formatResult(result, verbose));
|
|
1535
|
-
}
|
|
1536
|
-
});
|
|
1537
|
-
});
|
|
1538
|
-
}
|
|
1539
|
-
function formatResult(result, verbose) {
|
|
1540
|
-
if (verbose) {
|
|
1541
|
-
return JSON.stringify(result, null, 2);
|
|
1542
|
-
}
|
|
1543
|
-
const output = {
|
|
1544
|
-
status: result.status,
|
|
1545
|
-
exitCode: result.exitCode
|
|
1546
|
-
};
|
|
1547
|
-
if (result.stdout) {
|
|
1548
|
-
output.stdout = result.stdout;
|
|
1549
|
-
}
|
|
1550
|
-
if (result.stderr) {
|
|
1551
|
-
output.stderr = result.stderr;
|
|
1552
|
-
}
|
|
1553
|
-
if (result.status === "timeout") {
|
|
1554
|
-
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
1555
|
-
}
|
|
1556
|
-
if (result.status === "blocked" && result.blockedReason) {
|
|
1557
|
-
output.blockedReason = result.blockedReason;
|
|
1558
|
-
}
|
|
1559
|
-
if (result.truncated) {
|
|
1560
|
-
output.warning = "Output was truncated due to size limits (100KB max per stream)";
|
|
1561
|
-
}
|
|
1562
|
-
return JSON.stringify(output, null, 2);
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
1326
|
// src/app/agent/tools/natives/edit.ts
|
|
1566
1327
|
import path3 from "path";
|
|
1567
|
-
import
|
|
1328
|
+
import os from "os";
|
|
1568
1329
|
import { promises as fs2 } from "fs";
|
|
1569
1330
|
import { diffLines } from "diff";
|
|
1570
1331
|
var MAX_DIFF_SIZE = 5e4;
|
|
@@ -1572,7 +1333,7 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
1572
1333
|
function normalizePath(filePath) {
|
|
1573
1334
|
try {
|
|
1574
1335
|
filePath = filePath.trim();
|
|
1575
|
-
if (
|
|
1336
|
+
if (os.platform() === "win32") {
|
|
1576
1337
|
const winDriveRegex = /^\/([a-zA-Z])[:/]/;
|
|
1577
1338
|
const match = filePath.match(winDriveRegex);
|
|
1578
1339
|
if (match) {
|
|
@@ -2993,23 +2754,42 @@ async function viewFileOutline(args) {
|
|
|
2993
2754
|
}
|
|
2994
2755
|
|
|
2995
2756
|
// src/app/agent/tools/natives/async_command.ts
|
|
2996
|
-
import
|
|
2997
|
-
import { spawn
|
|
2757
|
+
import os2 from "os";
|
|
2758
|
+
import { spawn } from "child_process";
|
|
2998
2759
|
import { v4 as uuidv42 } from "uuid";
|
|
2999
2760
|
var runningCommands = /* @__PURE__ */ new Map();
|
|
3000
|
-
var
|
|
2761
|
+
var MAX_OUTPUT_SIZE = 3e4;
|
|
3001
2762
|
var MAX_STORED_COMMANDS = 50;
|
|
3002
|
-
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED]";
|
|
3003
|
-
var
|
|
2763
|
+
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED - 30KB/200 lines max]";
|
|
2764
|
+
var DANGEROUS_PATTERNS = [
|
|
2765
|
+
// Elevação de privilégios
|
|
3004
2766
|
/^sudo\s+/i,
|
|
3005
2767
|
/^doas\s+/i,
|
|
3006
2768
|
/^su\s+/i,
|
|
3007
|
-
|
|
2769
|
+
/^pkexec\s+/i,
|
|
2770
|
+
/\|\s*sudo\s+/i,
|
|
2771
|
+
/;\s*sudo\s+/i,
|
|
2772
|
+
/&&\s*sudo\s+/i,
|
|
2773
|
+
// Comandos destrutivos
|
|
2774
|
+
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
2775
|
+
/\brm\s+-[rf]*\s+\*/i,
|
|
2776
|
+
/\bchmod\s+(777|666)\s+\//i,
|
|
2777
|
+
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
2778
|
+
/\bmkfs\./i,
|
|
2779
|
+
// Fork bombs
|
|
2780
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
2781
|
+
// Remote code exec
|
|
2782
|
+
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
2783
|
+
/\bwget\s+.*\|\s*(ba)?sh/i
|
|
3008
2784
|
];
|
|
3009
|
-
var
|
|
3010
|
-
/^(vim|vi|nano|emacs|
|
|
3011
|
-
/^(
|
|
3012
|
-
/^(
|
|
2785
|
+
var INTERACTIVE_PATTERNS = [
|
|
2786
|
+
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
2787
|
+
/^(less|more|most)\s*/i,
|
|
2788
|
+
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
2789
|
+
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
2790
|
+
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
2791
|
+
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
2792
|
+
/^(gdb|lldb)\s*/i
|
|
3013
2793
|
];
|
|
3014
2794
|
function cleanupOldCommands() {
|
|
3015
2795
|
if (runningCommands.size <= MAX_STORED_COMMANDS) return;
|
|
@@ -3021,12 +2801,12 @@ function cleanupOldCommands() {
|
|
|
3021
2801
|
}
|
|
3022
2802
|
function isDangerousCommand(command) {
|
|
3023
2803
|
const trimmed = command.trim();
|
|
3024
|
-
for (const pattern of
|
|
2804
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
3025
2805
|
if (pattern.test(trimmed)) {
|
|
3026
2806
|
return "Command requires elevated privileges (sudo/doas) which cannot be handled in async mode";
|
|
3027
2807
|
}
|
|
3028
2808
|
}
|
|
3029
|
-
for (const pattern of
|
|
2809
|
+
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
3030
2810
|
if (pattern.test(trimmed)) {
|
|
3031
2811
|
return "Interactive commands are not supported in async mode";
|
|
3032
2812
|
}
|
|
@@ -3054,7 +2834,7 @@ async function runCommandAsync(args) {
|
|
|
3054
2834
|
};
|
|
3055
2835
|
}
|
|
3056
2836
|
const commandId = uuidv42().substring(0, 8);
|
|
3057
|
-
const platform =
|
|
2837
|
+
const platform = os2.platform();
|
|
3058
2838
|
let shellCmd;
|
|
3059
2839
|
let shellArgs;
|
|
3060
2840
|
if (platform === "win32") {
|
|
@@ -3074,7 +2854,7 @@ async function runCommandAsync(args) {
|
|
|
3074
2854
|
startTime: Date.now(),
|
|
3075
2855
|
process: null
|
|
3076
2856
|
};
|
|
3077
|
-
const child =
|
|
2857
|
+
const child = spawn(shellCmd, shellArgs, {
|
|
3078
2858
|
cwd,
|
|
3079
2859
|
env: process.env,
|
|
3080
2860
|
windowsHide: true
|
|
@@ -3084,8 +2864,8 @@ async function runCommandAsync(args) {
|
|
|
3084
2864
|
child.stdout?.on("data", (data) => {
|
|
3085
2865
|
if (!stdoutTruncated) {
|
|
3086
2866
|
entry.stdout += data.toString();
|
|
3087
|
-
if (entry.stdout.length >
|
|
3088
|
-
entry.stdout = entry.stdout.substring(0,
|
|
2867
|
+
if (entry.stdout.length > MAX_OUTPUT_SIZE) {
|
|
2868
|
+
entry.stdout = entry.stdout.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MSG;
|
|
3089
2869
|
stdoutTruncated = true;
|
|
3090
2870
|
}
|
|
3091
2871
|
}
|
|
@@ -3094,8 +2874,8 @@ async function runCommandAsync(args) {
|
|
|
3094
2874
|
child.stderr?.on("data", (data) => {
|
|
3095
2875
|
if (!stderrTruncated) {
|
|
3096
2876
|
entry.stderr += data.toString();
|
|
3097
|
-
if (entry.stderr.length >
|
|
3098
|
-
entry.stderr = entry.stderr.substring(0,
|
|
2877
|
+
if (entry.stderr.length > MAX_OUTPUT_SIZE) {
|
|
2878
|
+
entry.stderr = entry.stderr.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MSG;
|
|
3099
2879
|
stderrTruncated = true;
|
|
3100
2880
|
}
|
|
3101
2881
|
}
|
|
@@ -3280,12 +3060,12 @@ async function killCommand(args) {
|
|
|
3280
3060
|
// src/app/agent/tools/natives/task_boundary.ts
|
|
3281
3061
|
import path9 from "path";
|
|
3282
3062
|
import { promises as fs7 } from "fs";
|
|
3283
|
-
import
|
|
3063
|
+
import os3 from "os";
|
|
3284
3064
|
var currentTask = null;
|
|
3285
3065
|
var artifactsDir = null;
|
|
3286
3066
|
async function getArtifactsDir() {
|
|
3287
3067
|
if (artifactsDir) return artifactsDir;
|
|
3288
|
-
const homeDir =
|
|
3068
|
+
const homeDir = os3.homedir();
|
|
3289
3069
|
const baseDir = path9.join(homeDir, ".bluma", "artifacts");
|
|
3290
3070
|
const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
3291
3071
|
artifactsDir = path9.join(baseDir, sessionId2);
|
|
@@ -3640,7 +3420,6 @@ var ToolInvoker = class {
|
|
|
3640
3420
|
* Este método mapeia o nome da ferramenta (string) para a função TypeScript que a executa.
|
|
3641
3421
|
*/
|
|
3642
3422
|
registerTools() {
|
|
3643
|
-
this.toolImplementations.set("shell_command", shellCommand);
|
|
3644
3423
|
this.toolImplementations.set("edit_tool", editTool);
|
|
3645
3424
|
this.toolImplementations.set("ls_tool", ls);
|
|
3646
3425
|
this.toolImplementations.set("count_file_lines", countLines);
|
|
@@ -3648,7 +3427,7 @@ var ToolInvoker = class {
|
|
|
3648
3427
|
this.toolImplementations.set("find_by_name", findByName);
|
|
3649
3428
|
this.toolImplementations.set("grep_search", grepSearch);
|
|
3650
3429
|
this.toolImplementations.set("view_file_outline", viewFileOutline);
|
|
3651
|
-
this.toolImplementations.set("
|
|
3430
|
+
this.toolImplementations.set("shell_command", runCommandAsync);
|
|
3652
3431
|
this.toolImplementations.set("command_status", commandStatus);
|
|
3653
3432
|
this.toolImplementations.set("send_command_input", sendCommandInput);
|
|
3654
3433
|
this.toolImplementations.set("kill_command", killCommand);
|
|
@@ -3690,7 +3469,7 @@ var ToolInvoker = class {
|
|
|
3690
3469
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
3691
3470
|
import { promises as fs9 } from "fs";
|
|
3692
3471
|
import path11 from "path";
|
|
3693
|
-
import
|
|
3472
|
+
import os4 from "os";
|
|
3694
3473
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3695
3474
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3696
3475
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -3719,7 +3498,7 @@ var MCPClient = class {
|
|
|
3719
3498
|
const __filename = fileURLToPath2(import.meta.url);
|
|
3720
3499
|
const __dirname = path11.dirname(__filename);
|
|
3721
3500
|
const defaultConfigPath = path11.resolve(__dirname, "config", "bluma-mcp.json");
|
|
3722
|
-
const userConfigPath = path11.join(
|
|
3501
|
+
const userConfigPath = path11.join(os4.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
3723
3502
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
3724
3503
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
3725
3504
|
const mergedConfig = {
|
|
@@ -3772,7 +3551,7 @@ var MCPClient = class {
|
|
|
3772
3551
|
async connectToStdioServer(serverName, config2) {
|
|
3773
3552
|
let commandToExecute = config2.command;
|
|
3774
3553
|
let argsToExecute = config2.args || [];
|
|
3775
|
-
const isWindows =
|
|
3554
|
+
const isWindows = os4.platform() === "win32";
|
|
3776
3555
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
3777
3556
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
3778
3557
|
commandToExecute = argsToExecute[1];
|
|
@@ -3892,7 +3671,7 @@ import path14 from "path";
|
|
|
3892
3671
|
|
|
3893
3672
|
// src/app/agent/session_manager/session_manager.ts
|
|
3894
3673
|
import path12 from "path";
|
|
3895
|
-
import
|
|
3674
|
+
import os5 from "os";
|
|
3896
3675
|
import { promises as fs10 } from "fs";
|
|
3897
3676
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
3898
3677
|
async function withFileLock(file, fn) {
|
|
@@ -3908,15 +3687,32 @@ async function withFileLock(file, fn) {
|
|
|
3908
3687
|
if (fileLocks.get(file) === p) fileLocks.delete(file);
|
|
3909
3688
|
}
|
|
3910
3689
|
}
|
|
3690
|
+
var pendingSaves = /* @__PURE__ */ new Map();
|
|
3691
|
+
var DEBOUNCE_DELAY_MS = 100;
|
|
3692
|
+
function debouncedSave(sessionFile, history) {
|
|
3693
|
+
const existing = pendingSaves.get(sessionFile);
|
|
3694
|
+
if (existing) {
|
|
3695
|
+
clearTimeout(existing.timer);
|
|
3696
|
+
}
|
|
3697
|
+
const timer = setTimeout(async () => {
|
|
3698
|
+
pendingSaves.delete(sessionFile);
|
|
3699
|
+
try {
|
|
3700
|
+
await doSaveSessionHistory(sessionFile, history);
|
|
3701
|
+
} catch (e) {
|
|
3702
|
+
console.warn(`Debounced save failed for ${sessionFile}: ${e.message}`);
|
|
3703
|
+
}
|
|
3704
|
+
}, DEBOUNCE_DELAY_MS);
|
|
3705
|
+
pendingSaves.set(sessionFile, { history: [...history], timer });
|
|
3706
|
+
}
|
|
3911
3707
|
function expandHome(p) {
|
|
3912
3708
|
if (!p) return p;
|
|
3913
3709
|
if (p.startsWith("~")) {
|
|
3914
|
-
return path12.join(
|
|
3710
|
+
return path12.join(os5.homedir(), p.slice(1));
|
|
3915
3711
|
}
|
|
3916
3712
|
return p;
|
|
3917
3713
|
}
|
|
3918
3714
|
function getPreferredAppDir() {
|
|
3919
|
-
const fixed = path12.join(
|
|
3715
|
+
const fixed = path12.join(os5.homedir(), ".bluma-cli");
|
|
3920
3716
|
return path12.resolve(expandHome(fixed));
|
|
3921
3717
|
}
|
|
3922
3718
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
@@ -3925,25 +3721,36 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
3925
3721
|
const isWin = process.platform === "win32";
|
|
3926
3722
|
while (attempt <= maxRetries) {
|
|
3927
3723
|
try {
|
|
3724
|
+
const dir = path12.dirname(dest);
|
|
3725
|
+
await fs10.mkdir(dir, { recursive: true }).catch(() => {
|
|
3726
|
+
});
|
|
3928
3727
|
await fs10.rename(src, dest);
|
|
3929
3728
|
return;
|
|
3930
3729
|
} catch (e) {
|
|
3931
3730
|
lastErr = e;
|
|
3932
3731
|
const code = e && e.code || "";
|
|
3933
|
-
const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES";
|
|
3934
|
-
if (!
|
|
3732
|
+
const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES" || code === "ENOENT";
|
|
3733
|
+
if (!transient || attempt === maxRetries) break;
|
|
3935
3734
|
const backoff = Math.min(1e3, 50 * Math.pow(2, attempt));
|
|
3936
3735
|
await new Promise((r) => setTimeout(r, backoff));
|
|
3937
3736
|
attempt++;
|
|
3938
3737
|
}
|
|
3939
3738
|
}
|
|
3940
3739
|
try {
|
|
3740
|
+
await fs10.access(src);
|
|
3941
3741
|
const data = await fs10.readFile(src);
|
|
3742
|
+
const dir = path12.dirname(dest);
|
|
3743
|
+
await fs10.mkdir(dir, { recursive: true }).catch(() => {
|
|
3744
|
+
});
|
|
3942
3745
|
await fs10.writeFile(dest, data);
|
|
3943
3746
|
await fs10.unlink(src).catch(() => {
|
|
3944
3747
|
});
|
|
3945
3748
|
return;
|
|
3946
3749
|
} catch (fallbackErr) {
|
|
3750
|
+
if (fallbackErr?.code === "ENOENT") {
|
|
3751
|
+
console.warn(`Session temp file disappeared: ${src}`);
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3947
3754
|
throw lastErr || fallbackErr;
|
|
3948
3755
|
}
|
|
3949
3756
|
}
|
|
@@ -3971,7 +3778,7 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
3971
3778
|
return [sessionFile, [], []];
|
|
3972
3779
|
}
|
|
3973
3780
|
}
|
|
3974
|
-
async function
|
|
3781
|
+
async function doSaveSessionHistory(sessionFile, history) {
|
|
3975
3782
|
await withFileLock(sessionFile, async () => {
|
|
3976
3783
|
let sessionData;
|
|
3977
3784
|
try {
|
|
@@ -4021,9 +3828,12 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
4021
3828
|
}
|
|
4022
3829
|
});
|
|
4023
3830
|
}
|
|
3831
|
+
async function saveSessionHistory(sessionFile, history) {
|
|
3832
|
+
debouncedSave(sessionFile, history);
|
|
3833
|
+
}
|
|
4024
3834
|
|
|
4025
3835
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
4026
|
-
import
|
|
3836
|
+
import os6 from "os";
|
|
4027
3837
|
import fs11 from "fs";
|
|
4028
3838
|
import path13 from "path";
|
|
4029
3839
|
import { execSync } from "child_process";
|
|
@@ -4218,12 +4028,6 @@ You are a **teammate who writes tests**, not an assistant who skips them.
|
|
|
4218
4028
|
- **For edit_tool**: Provide exact content with correct whitespace (read first!)
|
|
4219
4029
|
- **Check file exists** before attempting edits
|
|
4220
4030
|
|
|
4221
|
-
### Shell Commands:
|
|
4222
|
-
- **NEVER use sudo** - Commands requiring sudo will be blocked
|
|
4223
|
-
- **Long commands**: Set appropriate timeout (default 300s)
|
|
4224
|
-
- **Interactive commands** (vim, less, ssh) are blocked - use alternatives
|
|
4225
|
-
- **Large outputs** will be truncated at 100KB
|
|
4226
|
-
|
|
4227
4031
|
### Safe Auto-Approved Tools (no confirmation needed):
|
|
4228
4032
|
- message_notify_user
|
|
4229
4033
|
- ls_tool
|
|
@@ -4239,6 +4043,108 @@ You are a **teammate who writes tests**, not an assistant who skips them.
|
|
|
4239
4043
|
|
|
4240
4044
|
---
|
|
4241
4045
|
|
|
4046
|
+
<shell_command>
|
|
4047
|
+
## Shell Command System (CRITICAL TO UNDERSTAND)
|
|
4048
|
+
|
|
4049
|
+
The \`shell_command\` tool runs commands in **background** and returns a \`command_id\`.
|
|
4050
|
+
You MUST use \`command_status\` to get the output.
|
|
4051
|
+
|
|
4052
|
+
### Workflow:
|
|
4053
|
+
\`\`\`
|
|
4054
|
+
1. shell_command({ command: "npm install" })
|
|
4055
|
+
\u2192 Returns: { success: true, command_id: "abc123" }
|
|
4056
|
+
|
|
4057
|
+
2. command_status({ command_id: "abc123", wait_seconds: 60 })
|
|
4058
|
+
\u2192 Returns: { status: "completed", stdout: "...", exit_code: 0 }
|
|
4059
|
+
\`\`\`
|
|
4060
|
+
|
|
4061
|
+
### Security Rules (BLOCKED COMMANDS):
|
|
4062
|
+
- [BLOCKED] \`sudo\`, \`doas\`, \`su\`, \`pkexec\` - Elevation blocked
|
|
4063
|
+
- [BLOCKED] \`rm -rf /\`, \`rm -rf *\`, \`rm -rf ~\` - Destructive
|
|
4064
|
+
- [BLOCKED] \`chmod 777 /\`, \`chmod 666 /\` - Insecure permissions
|
|
4065
|
+
- [BLOCKED] \`curl ... | bash\`, \`wget ... | sh\` - Remote code execution
|
|
4066
|
+
- [BLOCKED] \`vim\`, \`nano\`, \`less\`, \`ssh\`, \`mysql\` - Interactive prompts
|
|
4067
|
+
- [BLOCKED] Fork bombs, \`dd\` to disk, \`mkfs.*\` - System destruction
|
|
4068
|
+
|
|
4069
|
+
### Output Limits:
|
|
4070
|
+
- Maximum 30KB or 200 lines per output
|
|
4071
|
+
- Long outputs are truncated (head + tail shown)
|
|
4072
|
+
- For large outputs, use: \`command | head -n 50\` or redirect to file
|
|
4073
|
+
|
|
4074
|
+
### Examples:
|
|
4075
|
+
|
|
4076
|
+
**[OK] Install dependencies:**
|
|
4077
|
+
\`\`\`
|
|
4078
|
+
[Step 1] shell_command({ command: "npm install", cwd: "/project" })
|
|
4079
|
+
\u2192 { command_id: "cmd_001" }
|
|
4080
|
+
|
|
4081
|
+
[Step 2] command_status({ command_id: "cmd_001", wait_seconds: 120 })
|
|
4082
|
+
\u2192 { status: "completed", exit_code: 0, stdout: "added 150 packages..." }
|
|
4083
|
+
|
|
4084
|
+
[Step 3] message_notify_user("Dependencies installed successfully.")
|
|
4085
|
+
\`\`\`
|
|
4086
|
+
|
|
4087
|
+
**[OK] Run tests:**
|
|
4088
|
+
\`\`\`
|
|
4089
|
+
[Step 1] shell_command({ command: "npm test" })
|
|
4090
|
+
\u2192 { command_id: "cmd_002" }
|
|
4091
|
+
|
|
4092
|
+
[Step 2] command_status({ command_id: "cmd_002", wait_seconds: 60 })
|
|
4093
|
+
\u2192 { status: "completed", exit_code: 0, stdout: "47 tests passed" }
|
|
4094
|
+
\`\`\`
|
|
4095
|
+
|
|
4096
|
+
**[OK] Quick command (git status):**
|
|
4097
|
+
\`\`\`
|
|
4098
|
+
[Step 1] shell_command({ command: "git status --short" })
|
|
4099
|
+
\u2192 { command_id: "cmd_003" }
|
|
4100
|
+
|
|
4101
|
+
[Step 2] command_status({ command_id: "cmd_003", wait_seconds: 5 })
|
|
4102
|
+
\u2192 { status: "completed", stdout: "M src/index.ts\\nA src/utils.ts" }
|
|
4103
|
+
\`\`\`
|
|
4104
|
+
|
|
4105
|
+
**[OK] Long build with timeout:**
|
|
4106
|
+
\`\`\`
|
|
4107
|
+
[Step 1] shell_command({ command: "docker build -t myapp .", timeout: 600 })
|
|
4108
|
+
\u2192 { command_id: "cmd_004" }
|
|
4109
|
+
|
|
4110
|
+
[Step 2] command_status({ command_id: "cmd_004", wait_seconds: 10 })
|
|
4111
|
+
\u2192 { status: "running" }
|
|
4112
|
+
|
|
4113
|
+
[Step 3] message_notify_user("Building Docker image, please wait...")
|
|
4114
|
+
|
|
4115
|
+
[Step 4] command_status({ command_id: "cmd_004", wait_seconds: 10 })
|
|
4116
|
+
\u2192 { status: "completed", exit_code: 0, stdout: "Successfully built abc123" }
|
|
4117
|
+
\`\`\`
|
|
4118
|
+
|
|
4119
|
+
**[WRONG] Never forget command_status:**
|
|
4120
|
+
\`\`\`
|
|
4121
|
+
shell_command({ command: "npm install" })
|
|
4122
|
+
message_notify_user("Installed!") // WRONG: You don't know if it succeeded!
|
|
4123
|
+
\`\`\`
|
|
4124
|
+
|
|
4125
|
+
**[WRONG] Never use long waits (blocks user):**
|
|
4126
|
+
\`\`\`
|
|
4127
|
+
command_status({ id: "...", wait_seconds: 300 }) // WRONG: User can't interrupt!
|
|
4128
|
+
\`\`\`
|
|
4129
|
+
|
|
4130
|
+
**[WRONG] Never use blocked commands:**
|
|
4131
|
+
\`\`\`
|
|
4132
|
+
shell_command({ command: "sudo apt install ..." }) // BLOCKED
|
|
4133
|
+
shell_command({ command: "rm -rf /" }) // BLOCKED
|
|
4134
|
+
shell_command({ command: "vim file.txt" }) // BLOCKED (interactive)
|
|
4135
|
+
\`\`\`
|
|
4136
|
+
|
|
4137
|
+
### Tips:
|
|
4138
|
+
- Always check \`exit_code\` - 0 means success, non-zero is error
|
|
4139
|
+
- Use wait_seconds: 5-10 for polling (allows user interrupts)
|
|
4140
|
+
- If \`status: "running"\`, notify user and poll again
|
|
4141
|
+
- If \`truncated: true\`, output was too long - use \`| head\` or \`| tail\`
|
|
4142
|
+
- Use \`send_command_input\` for commands that need input (y/n prompts)
|
|
4143
|
+
- Use \`kill_command\` to stop a stuck command
|
|
4144
|
+
</shell_command>
|
|
4145
|
+
|
|
4146
|
+
---
|
|
4147
|
+
|
|
4242
4148
|
<communication_style>
|
|
4243
4149
|
## How You Communicate
|
|
4244
4150
|
|
|
@@ -4330,12 +4236,12 @@ Let's build something great, {username}.
|
|
|
4330
4236
|
function getUnifiedSystemPrompt() {
|
|
4331
4237
|
const cwd = process.cwd();
|
|
4332
4238
|
const env = {
|
|
4333
|
-
os_type:
|
|
4334
|
-
os_version:
|
|
4335
|
-
architecture:
|
|
4239
|
+
os_type: os6.type(),
|
|
4240
|
+
os_version: os6.release(),
|
|
4241
|
+
architecture: os6.arch(),
|
|
4336
4242
|
workdir: cwd,
|
|
4337
4243
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
4338
|
-
username:
|
|
4244
|
+
username: os6.userInfo().username,
|
|
4339
4245
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
4340
4246
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
4341
4247
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -4721,7 +4627,7 @@ function getSubAgentByCommand(cmd) {
|
|
|
4721
4627
|
}
|
|
4722
4628
|
|
|
4723
4629
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
4724
|
-
import
|
|
4630
|
+
import os7 from "os";
|
|
4725
4631
|
var SYSTEM_PROMPT2 = `
|
|
4726
4632
|
|
|
4727
4633
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -4884,12 +4790,12 @@ Rule Summary:
|
|
|
4884
4790
|
function getInitPrompt() {
|
|
4885
4791
|
const now = /* @__PURE__ */ new Date();
|
|
4886
4792
|
const collectedData = {
|
|
4887
|
-
os_type:
|
|
4888
|
-
os_version:
|
|
4889
|
-
architecture:
|
|
4793
|
+
os_type: os7.type(),
|
|
4794
|
+
os_version: os7.release(),
|
|
4795
|
+
architecture: os7.arch(),
|
|
4890
4796
|
workdir: process.cwd(),
|
|
4891
4797
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
4892
|
-
username:
|
|
4798
|
+
username: os7.userInfo().username || "Unknown",
|
|
4893
4799
|
current_date: now.toISOString().split("T")[0],
|
|
4894
4800
|
// Formato YYYY-MM-DD
|
|
4895
4801
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -5142,7 +5048,7 @@ var SubAgentsBluMa = class {
|
|
|
5142
5048
|
};
|
|
5143
5049
|
|
|
5144
5050
|
// src/app/agent/agent.ts
|
|
5145
|
-
var globalEnvPath = path15.join(
|
|
5051
|
+
var globalEnvPath = path15.join(os8.homedir(), ".bluma-cli", ".env");
|
|
5146
5052
|
dotenv.config({ path: globalEnvPath });
|
|
5147
5053
|
var Agent = class {
|
|
5148
5054
|
sessionId;
|
|
@@ -5520,18 +5426,6 @@ var renderViewFileOutline = ({ args }) => {
|
|
|
5520
5426
|
] })
|
|
5521
5427
|
] });
|
|
5522
5428
|
};
|
|
5523
|
-
var renderRunCommandAsync = ({ args }) => {
|
|
5524
|
-
const parsed = parseArgs(args);
|
|
5525
|
-
const command = parsed.command || "[no command]";
|
|
5526
|
-
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5527
|
-
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "async" }),
|
|
5528
|
-
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: " $" }),
|
|
5529
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5530
|
-
" ",
|
|
5531
|
-
truncate(command, 50)
|
|
5532
|
-
] })
|
|
5533
|
-
] });
|
|
5534
|
-
};
|
|
5535
5429
|
var renderCommandStatus = ({ args }) => {
|
|
5536
5430
|
const parsed = parseArgs(args);
|
|
5537
5431
|
const id = parsed.command_id || "[no id]";
|
|
@@ -5607,7 +5501,6 @@ var ToolRenderDisplay = {
|
|
|
5607
5501
|
find_by_name: renderFindByName,
|
|
5608
5502
|
grep_search: renderGrepSearch,
|
|
5609
5503
|
view_file_outline: renderViewFileOutline,
|
|
5610
|
-
run_command_async: renderRunCommandAsync,
|
|
5611
5504
|
command_status: renderCommandStatus,
|
|
5612
5505
|
task_boundary: renderTaskBoundary,
|
|
5613
5506
|
search_web: renderSearchWeb
|
|
@@ -5837,10 +5730,20 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
5837
5730
|
] })
|
|
5838
5731
|
] });
|
|
5839
5732
|
}
|
|
5840
|
-
if ((toolName.includes("shell_command") || toolName.includes("run_command")) && parsed) {
|
|
5733
|
+
if ((toolName.includes("shell_command") || toolName.includes("run_command") || toolName.includes("command_status")) && parsed) {
|
|
5841
5734
|
const output = parsed.stdout || parsed.output || "";
|
|
5842
5735
|
const stderr = parsed.stderr || "";
|
|
5843
5736
|
const exitCode = parsed.exit_code ?? parsed.exitCode ?? 0;
|
|
5737
|
+
const status = parsed.status || "";
|
|
5738
|
+
if (parsed.command_id && !output && !stderr && !status) {
|
|
5739
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5740
|
+
"started #",
|
|
5741
|
+
parsed.command_id.slice(0, 8)
|
|
5742
|
+
] }) });
|
|
5743
|
+
}
|
|
5744
|
+
if (status === "running") {
|
|
5745
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "yellow", children: "still running..." }) });
|
|
5746
|
+
}
|
|
5844
5747
|
if (!output && !stderr) return null;
|
|
5845
5748
|
const { lines, truncated } = truncateLines(output || stderr, MAX_LINES);
|
|
5846
5749
|
const isError = exitCode !== 0 || stderr;
|
|
@@ -5850,6 +5753,10 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
5850
5753
|
"... +",
|
|
5851
5754
|
truncated,
|
|
5852
5755
|
" lines"
|
|
5756
|
+
] }),
|
|
5757
|
+
exitCode !== 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
|
|
5758
|
+
"exit code: ",
|
|
5759
|
+
exitCode
|
|
5853
5760
|
] })
|
|
5854
5761
|
] });
|
|
5855
5762
|
}
|