@nomad-e/bluma-cli 0.0.105 → 0.0.107
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.
|
@@ -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
|
@@ -172,20 +172,13 @@ import { Box as Box16, Text as Text15, Static } from "ink";
|
|
|
172
172
|
import { Box, Text } from "ink";
|
|
173
173
|
import { memo } from "react";
|
|
174
174
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
175
|
-
var VERSION = "0.0.103";
|
|
176
175
|
var HeaderComponent = ({
|
|
177
176
|
sessionId: sessionId2,
|
|
178
177
|
workdir
|
|
179
178
|
}) => {
|
|
180
179
|
const dirName = workdir.split("/").pop() || workdir;
|
|
181
180
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
182
|
-
/* @__PURE__ */
|
|
183
|
-
/* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "bluma \u2014 coding agent" }),
|
|
184
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
185
|
-
" ",
|
|
186
|
-
VERSION
|
|
187
|
-
] })
|
|
188
|
-
] }),
|
|
181
|
+
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "magenta", bold: true, children: "bluma \u2014 coding agent" }) }),
|
|
189
182
|
/* @__PURE__ */ jsxs(Box, { children: [
|
|
190
183
|
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "cwd " }),
|
|
191
184
|
/* @__PURE__ */ jsx(Text, { color: "cyan", children: dirName }),
|
|
@@ -741,7 +734,7 @@ var TextLine = memo2(({
|
|
|
741
734
|
const after = line.slice(cursorCol + 1);
|
|
742
735
|
return /* @__PURE__ */ jsxs2(Text2, { children: [
|
|
743
736
|
before,
|
|
744
|
-
/* @__PURE__ */ jsx2(Text2, { inverse: true, color: "
|
|
737
|
+
/* @__PURE__ */ jsx2(Text2, { inverse: true, color: "white", children: char }),
|
|
745
738
|
after
|
|
746
739
|
] });
|
|
747
740
|
}, (prev, next) => {
|
|
@@ -1323,255 +1316,16 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
1323
1316
|
import OpenAI from "openai";
|
|
1324
1317
|
import * as dotenv from "dotenv";
|
|
1325
1318
|
import path15 from "path";
|
|
1326
|
-
import
|
|
1319
|
+
import os8 from "os";
|
|
1327
1320
|
|
|
1328
1321
|
// src/app/agent/tool_invoker.ts
|
|
1329
1322
|
import { promises as fs8 } from "fs";
|
|
1330
1323
|
import path10 from "path";
|
|
1331
1324
|
import { fileURLToPath } from "url";
|
|
1332
1325
|
|
|
1333
|
-
// src/app/agent/tools/natives/shell_command.ts
|
|
1334
|
-
import os from "os";
|
|
1335
|
-
import { spawn } from "child_process";
|
|
1336
|
-
var MAX_OUTPUT_SIZE = 1e5;
|
|
1337
|
-
var OUTPUT_TRUNCATION_MESSAGE = "\n\n[OUTPUT TRUNCATED - exceeded 100KB limit. Use pagination or redirect to file for full output.]";
|
|
1338
|
-
var DANGEROUS_PATTERNS = [
|
|
1339
|
-
// === ELEVAÇÃO DE PRIVILÉGIOS ===
|
|
1340
|
-
/^sudo\s+/i,
|
|
1341
|
-
// sudo commands
|
|
1342
|
-
/^doas\s+/i,
|
|
1343
|
-
// doas (BSD sudo alternative)
|
|
1344
|
-
/^su\s+/i,
|
|
1345
|
-
// switch user
|
|
1346
|
-
/^pkexec\s+/i,
|
|
1347
|
-
// PolicyKit execution
|
|
1348
|
-
/\|\s*sudo\s+/i,
|
|
1349
|
-
// piped to sudo
|
|
1350
|
-
/;\s*sudo\s+/i,
|
|
1351
|
-
// chained with sudo
|
|
1352
|
-
/&&\s*sudo\s+/i,
|
|
1353
|
-
// AND chained with sudo
|
|
1354
|
-
// === COMANDOS DESTRUTIVOS ===
|
|
1355
|
-
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
1356
|
-
// rm com paths perigosos (/, ~)
|
|
1357
|
-
/\brm\s+-[rf]*\s+\*/i,
|
|
1358
|
-
// rm -rf *
|
|
1359
|
-
/\bchmod\s+(777|666)\s+\//i,
|
|
1360
|
-
// chmod 777/666 em paths root
|
|
1361
|
-
/\bchown\s+.*\s+\//i,
|
|
1362
|
-
// chown em paths root
|
|
1363
|
-
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
1364
|
-
// dd para discos
|
|
1365
|
-
/\bmkfs\./i,
|
|
1366
|
-
// format filesystem
|
|
1367
|
-
/>\s*\/dev\/(sd|hd|nvme)/i,
|
|
1368
|
-
// redirect para disco
|
|
1369
|
-
/\bshred\s+/i,
|
|
1370
|
-
// secure delete
|
|
1371
|
-
// === FORK BOMB / RESOURCE EXHAUSTION ===
|
|
1372
|
-
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
1373
|
-
// fork bomb clássico
|
|
1374
|
-
/\bwhile\s+true\s*;\s*do/i,
|
|
1375
|
-
// infinite loop
|
|
1376
|
-
/\byes\s+\|/i,
|
|
1377
|
-
// yes piped (resource exhaustion)
|
|
1378
|
-
// === NETWORK PERIGOSO ===
|
|
1379
|
-
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
1380
|
-
// curl | bash (remote code exec)
|
|
1381
|
-
/\bwget\s+.*\|\s*(ba)?sh/i,
|
|
1382
|
-
// wget | bash
|
|
1383
|
-
/\bnc\s+-[el]/i
|
|
1384
|
-
// netcat listener (backdoor)
|
|
1385
|
-
];
|
|
1386
|
-
var INTERACTIVE_PATTERNS = [
|
|
1387
|
-
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
1388
|
-
// editors
|
|
1389
|
-
/^(less|more|most)\s*/i,
|
|
1390
|
-
// pagers
|
|
1391
|
-
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
1392
|
-
// monitoring tools
|
|
1393
|
-
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
1394
|
-
// remote connections
|
|
1395
|
-
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
1396
|
-
// database CLIs (sem script)
|
|
1397
|
-
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
1398
|
-
// REPLs sem script
|
|
1399
|
-
/^(gdb|lldb)\s*/i,
|
|
1400
|
-
// debuggers
|
|
1401
|
-
/^(bc|dc)\s*$/i
|
|
1402
|
-
// calculators
|
|
1403
|
-
];
|
|
1404
|
-
function shellCommand(args) {
|
|
1405
|
-
const {
|
|
1406
|
-
command,
|
|
1407
|
-
timeout = 300,
|
|
1408
|
-
// 5 minutos por padrão
|
|
1409
|
-
cwd = process.cwd(),
|
|
1410
|
-
verbose = false
|
|
1411
|
-
} = args;
|
|
1412
|
-
return new Promise((resolve) => {
|
|
1413
|
-
const startTime = Date.now();
|
|
1414
|
-
const platform = os.platform();
|
|
1415
|
-
const commandTrimmed = command.trim();
|
|
1416
|
-
for (const pattern of DANGEROUS_PATTERNS) {
|
|
1417
|
-
if (pattern.test(commandTrimmed)) {
|
|
1418
|
-
const result = {
|
|
1419
|
-
status: "blocked",
|
|
1420
|
-
exitCode: null,
|
|
1421
|
-
stdout: "",
|
|
1422
|
-
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
1423
|
-
1. Run the command manually in terminal
|
|
1424
|
-
2. Configure passwordless sudo for this specific command
|
|
1425
|
-
3. Run BluMa as root (not recommended)`,
|
|
1426
|
-
command,
|
|
1427
|
-
cwd,
|
|
1428
|
-
platform,
|
|
1429
|
-
duration: Date.now() - startTime,
|
|
1430
|
-
blockedReason: "requires_sudo"
|
|
1431
|
-
};
|
|
1432
|
-
return resolve(formatResult(result, verbose));
|
|
1433
|
-
}
|
|
1434
|
-
}
|
|
1435
|
-
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
1436
|
-
if (pattern.test(commandTrimmed)) {
|
|
1437
|
-
const result = {
|
|
1438
|
-
status: "blocked",
|
|
1439
|
-
exitCode: null,
|
|
1440
|
-
stdout: "",
|
|
1441
|
-
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
1442
|
-
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
1443
|
-
2. Run the command manually in terminal
|
|
1444
|
-
3. For editors, use edit_tool instead`,
|
|
1445
|
-
command,
|
|
1446
|
-
cwd,
|
|
1447
|
-
platform,
|
|
1448
|
-
duration: Date.now() - startTime,
|
|
1449
|
-
blockedReason: "interactive_command"
|
|
1450
|
-
};
|
|
1451
|
-
return resolve(formatResult(result, verbose));
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
|
-
let shellCmd;
|
|
1455
|
-
let shellArgs;
|
|
1456
|
-
if (platform === "win32") {
|
|
1457
|
-
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
1458
|
-
shellArgs = ["/c", command];
|
|
1459
|
-
} else {
|
|
1460
|
-
shellCmd = process.env.SHELL || "/bin/bash";
|
|
1461
|
-
shellArgs = ["-c", command];
|
|
1462
|
-
}
|
|
1463
|
-
let stdout = "";
|
|
1464
|
-
let stderr = "";
|
|
1465
|
-
let stdoutTruncated = false;
|
|
1466
|
-
let stderrTruncated = false;
|
|
1467
|
-
let timedOut = false;
|
|
1468
|
-
let finished = false;
|
|
1469
|
-
const childProcess = spawn(shellCmd, shellArgs, {
|
|
1470
|
-
cwd,
|
|
1471
|
-
env: process.env,
|
|
1472
|
-
// Importante: no Windows, precisamos do shell, mas spawn já lida com isso
|
|
1473
|
-
windowsHide: true
|
|
1474
|
-
});
|
|
1475
|
-
const timeoutId = setTimeout(() => {
|
|
1476
|
-
if (!finished) {
|
|
1477
|
-
timedOut = true;
|
|
1478
|
-
childProcess.kill("SIGTERM");
|
|
1479
|
-
setTimeout(() => {
|
|
1480
|
-
if (!finished) {
|
|
1481
|
-
childProcess.kill("SIGKILL");
|
|
1482
|
-
}
|
|
1483
|
-
}, 2e3);
|
|
1484
|
-
}
|
|
1485
|
-
}, timeout * 1e3);
|
|
1486
|
-
if (childProcess.stdout) {
|
|
1487
|
-
childProcess.stdout.on("data", (data) => {
|
|
1488
|
-
if (!stdoutTruncated) {
|
|
1489
|
-
stdout += data.toString();
|
|
1490
|
-
if (stdout.length > MAX_OUTPUT_SIZE) {
|
|
1491
|
-
stdout = stdout.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1492
|
-
stdoutTruncated = true;
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
});
|
|
1496
|
-
}
|
|
1497
|
-
if (childProcess.stderr) {
|
|
1498
|
-
childProcess.stderr.on("data", (data) => {
|
|
1499
|
-
if (!stderrTruncated) {
|
|
1500
|
-
stderr += data.toString();
|
|
1501
|
-
if (stderr.length > MAX_OUTPUT_SIZE) {
|
|
1502
|
-
stderr = stderr.substring(0, MAX_OUTPUT_SIZE) + OUTPUT_TRUNCATION_MESSAGE;
|
|
1503
|
-
stderrTruncated = true;
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
});
|
|
1507
|
-
}
|
|
1508
|
-
childProcess.on("error", (error) => {
|
|
1509
|
-
if (!finished) {
|
|
1510
|
-
finished = true;
|
|
1511
|
-
clearTimeout(timeoutId);
|
|
1512
|
-
const result = {
|
|
1513
|
-
status: "error",
|
|
1514
|
-
exitCode: null,
|
|
1515
|
-
stdout: stdout.trim(),
|
|
1516
|
-
stderr: `Failed to execute command: ${error.message}`,
|
|
1517
|
-
command,
|
|
1518
|
-
cwd,
|
|
1519
|
-
platform,
|
|
1520
|
-
duration: Date.now() - startTime
|
|
1521
|
-
};
|
|
1522
|
-
resolve(formatResult(result, verbose));
|
|
1523
|
-
}
|
|
1524
|
-
});
|
|
1525
|
-
childProcess.on("close", (code, signal) => {
|
|
1526
|
-
if (!finished) {
|
|
1527
|
-
finished = true;
|
|
1528
|
-
clearTimeout(timeoutId);
|
|
1529
|
-
const result = {
|
|
1530
|
-
status: timedOut ? "timeout" : code === 0 ? "success" : "error",
|
|
1531
|
-
exitCode: code,
|
|
1532
|
-
stdout: stdout.trim(),
|
|
1533
|
-
stderr: timedOut ? `Command timed out after ${timeout} seconds
|
|
1534
|
-
${stderr.trim()}` : stderr.trim(),
|
|
1535
|
-
command,
|
|
1536
|
-
cwd,
|
|
1537
|
-
platform,
|
|
1538
|
-
duration: Date.now() - startTime,
|
|
1539
|
-
truncated: stdoutTruncated || stderrTruncated
|
|
1540
|
-
};
|
|
1541
|
-
resolve(formatResult(result, verbose));
|
|
1542
|
-
}
|
|
1543
|
-
});
|
|
1544
|
-
});
|
|
1545
|
-
}
|
|
1546
|
-
function formatResult(result, verbose) {
|
|
1547
|
-
if (verbose) {
|
|
1548
|
-
return JSON.stringify(result, null, 2);
|
|
1549
|
-
}
|
|
1550
|
-
const output = {
|
|
1551
|
-
status: result.status,
|
|
1552
|
-
exitCode: result.exitCode
|
|
1553
|
-
};
|
|
1554
|
-
if (result.stdout) {
|
|
1555
|
-
output.stdout = result.stdout;
|
|
1556
|
-
}
|
|
1557
|
-
if (result.stderr) {
|
|
1558
|
-
output.stderr = result.stderr;
|
|
1559
|
-
}
|
|
1560
|
-
if (result.status === "timeout") {
|
|
1561
|
-
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
1562
|
-
}
|
|
1563
|
-
if (result.status === "blocked" && result.blockedReason) {
|
|
1564
|
-
output.blockedReason = result.blockedReason;
|
|
1565
|
-
}
|
|
1566
|
-
if (result.truncated) {
|
|
1567
|
-
output.warning = "Output was truncated due to size limits (100KB max per stream)";
|
|
1568
|
-
}
|
|
1569
|
-
return JSON.stringify(output, null, 2);
|
|
1570
|
-
}
|
|
1571
|
-
|
|
1572
1326
|
// src/app/agent/tools/natives/edit.ts
|
|
1573
1327
|
import path3 from "path";
|
|
1574
|
-
import
|
|
1328
|
+
import os from "os";
|
|
1575
1329
|
import { promises as fs2 } from "fs";
|
|
1576
1330
|
import { diffLines } from "diff";
|
|
1577
1331
|
var MAX_DIFF_SIZE = 5e4;
|
|
@@ -1579,7 +1333,7 @@ var MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
|
1579
1333
|
function normalizePath(filePath) {
|
|
1580
1334
|
try {
|
|
1581
1335
|
filePath = filePath.trim();
|
|
1582
|
-
if (
|
|
1336
|
+
if (os.platform() === "win32") {
|
|
1583
1337
|
const winDriveRegex = /^\/([a-zA-Z])[:/]/;
|
|
1584
1338
|
const match = filePath.match(winDriveRegex);
|
|
1585
1339
|
if (match) {
|
|
@@ -3000,23 +2754,42 @@ async function viewFileOutline(args) {
|
|
|
3000
2754
|
}
|
|
3001
2755
|
|
|
3002
2756
|
// src/app/agent/tools/natives/async_command.ts
|
|
3003
|
-
import
|
|
3004
|
-
import { spawn
|
|
2757
|
+
import os2 from "os";
|
|
2758
|
+
import { spawn } from "child_process";
|
|
3005
2759
|
import { v4 as uuidv42 } from "uuid";
|
|
3006
2760
|
var runningCommands = /* @__PURE__ */ new Map();
|
|
3007
|
-
var
|
|
2761
|
+
var MAX_OUTPUT_SIZE = 3e4;
|
|
3008
2762
|
var MAX_STORED_COMMANDS = 50;
|
|
3009
|
-
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED]";
|
|
3010
|
-
var
|
|
2763
|
+
var OUTPUT_TRUNCATION_MSG = "\n[OUTPUT TRUNCATED - 30KB/200 lines max]";
|
|
2764
|
+
var DANGEROUS_PATTERNS = [
|
|
2765
|
+
// Elevação de privilégios
|
|
3011
2766
|
/^sudo\s+/i,
|
|
3012
2767
|
/^doas\s+/i,
|
|
3013
2768
|
/^su\s+/i,
|
|
3014
|
-
|
|
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
|
|
3015
2784
|
];
|
|
3016
|
-
var
|
|
3017
|
-
/^(vim|vi|nano|emacs|
|
|
3018
|
-
/^(
|
|
3019
|
-
/^(
|
|
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
|
|
3020
2793
|
];
|
|
3021
2794
|
function cleanupOldCommands() {
|
|
3022
2795
|
if (runningCommands.size <= MAX_STORED_COMMANDS) return;
|
|
@@ -3028,12 +2801,12 @@ function cleanupOldCommands() {
|
|
|
3028
2801
|
}
|
|
3029
2802
|
function isDangerousCommand(command) {
|
|
3030
2803
|
const trimmed = command.trim();
|
|
3031
|
-
for (const pattern of
|
|
2804
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
3032
2805
|
if (pattern.test(trimmed)) {
|
|
3033
2806
|
return "Command requires elevated privileges (sudo/doas) which cannot be handled in async mode";
|
|
3034
2807
|
}
|
|
3035
2808
|
}
|
|
3036
|
-
for (const pattern of
|
|
2809
|
+
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
3037
2810
|
if (pattern.test(trimmed)) {
|
|
3038
2811
|
return "Interactive commands are not supported in async mode";
|
|
3039
2812
|
}
|
|
@@ -3061,7 +2834,7 @@ async function runCommandAsync(args) {
|
|
|
3061
2834
|
};
|
|
3062
2835
|
}
|
|
3063
2836
|
const commandId = uuidv42().substring(0, 8);
|
|
3064
|
-
const platform =
|
|
2837
|
+
const platform = os2.platform();
|
|
3065
2838
|
let shellCmd;
|
|
3066
2839
|
let shellArgs;
|
|
3067
2840
|
if (platform === "win32") {
|
|
@@ -3081,7 +2854,7 @@ async function runCommandAsync(args) {
|
|
|
3081
2854
|
startTime: Date.now(),
|
|
3082
2855
|
process: null
|
|
3083
2856
|
};
|
|
3084
|
-
const child =
|
|
2857
|
+
const child = spawn(shellCmd, shellArgs, {
|
|
3085
2858
|
cwd,
|
|
3086
2859
|
env: process.env,
|
|
3087
2860
|
windowsHide: true
|
|
@@ -3091,8 +2864,8 @@ async function runCommandAsync(args) {
|
|
|
3091
2864
|
child.stdout?.on("data", (data) => {
|
|
3092
2865
|
if (!stdoutTruncated) {
|
|
3093
2866
|
entry.stdout += data.toString();
|
|
3094
|
-
if (entry.stdout.length >
|
|
3095
|
-
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;
|
|
3096
2869
|
stdoutTruncated = true;
|
|
3097
2870
|
}
|
|
3098
2871
|
}
|
|
@@ -3101,8 +2874,8 @@ async function runCommandAsync(args) {
|
|
|
3101
2874
|
child.stderr?.on("data", (data) => {
|
|
3102
2875
|
if (!stderrTruncated) {
|
|
3103
2876
|
entry.stderr += data.toString();
|
|
3104
|
-
if (entry.stderr.length >
|
|
3105
|
-
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;
|
|
3106
2879
|
stderrTruncated = true;
|
|
3107
2880
|
}
|
|
3108
2881
|
}
|
|
@@ -3287,12 +3060,12 @@ async function killCommand(args) {
|
|
|
3287
3060
|
// src/app/agent/tools/natives/task_boundary.ts
|
|
3288
3061
|
import path9 from "path";
|
|
3289
3062
|
import { promises as fs7 } from "fs";
|
|
3290
|
-
import
|
|
3063
|
+
import os3 from "os";
|
|
3291
3064
|
var currentTask = null;
|
|
3292
3065
|
var artifactsDir = null;
|
|
3293
3066
|
async function getArtifactsDir() {
|
|
3294
3067
|
if (artifactsDir) return artifactsDir;
|
|
3295
|
-
const homeDir =
|
|
3068
|
+
const homeDir = os3.homedir();
|
|
3296
3069
|
const baseDir = path9.join(homeDir, ".bluma", "artifacts");
|
|
3297
3070
|
const sessionId2 = Date.now().toString(36) + Math.random().toString(36).substr(2, 5);
|
|
3298
3071
|
artifactsDir = path9.join(baseDir, sessionId2);
|
|
@@ -3647,7 +3420,6 @@ var ToolInvoker = class {
|
|
|
3647
3420
|
* Este método mapeia o nome da ferramenta (string) para a função TypeScript que a executa.
|
|
3648
3421
|
*/
|
|
3649
3422
|
registerTools() {
|
|
3650
|
-
this.toolImplementations.set("shell_command", shellCommand);
|
|
3651
3423
|
this.toolImplementations.set("edit_tool", editTool);
|
|
3652
3424
|
this.toolImplementations.set("ls_tool", ls);
|
|
3653
3425
|
this.toolImplementations.set("count_file_lines", countLines);
|
|
@@ -3655,7 +3427,7 @@ var ToolInvoker = class {
|
|
|
3655
3427
|
this.toolImplementations.set("find_by_name", findByName);
|
|
3656
3428
|
this.toolImplementations.set("grep_search", grepSearch);
|
|
3657
3429
|
this.toolImplementations.set("view_file_outline", viewFileOutline);
|
|
3658
|
-
this.toolImplementations.set("
|
|
3430
|
+
this.toolImplementations.set("shell_command", runCommandAsync);
|
|
3659
3431
|
this.toolImplementations.set("command_status", commandStatus);
|
|
3660
3432
|
this.toolImplementations.set("send_command_input", sendCommandInput);
|
|
3661
3433
|
this.toolImplementations.set("kill_command", killCommand);
|
|
@@ -3697,7 +3469,7 @@ var ToolInvoker = class {
|
|
|
3697
3469
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
3698
3470
|
import { promises as fs9 } from "fs";
|
|
3699
3471
|
import path11 from "path";
|
|
3700
|
-
import
|
|
3472
|
+
import os4 from "os";
|
|
3701
3473
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3702
3474
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
3703
3475
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -3726,7 +3498,7 @@ var MCPClient = class {
|
|
|
3726
3498
|
const __filename = fileURLToPath2(import.meta.url);
|
|
3727
3499
|
const __dirname = path11.dirname(__filename);
|
|
3728
3500
|
const defaultConfigPath = path11.resolve(__dirname, "config", "bluma-mcp.json");
|
|
3729
|
-
const userConfigPath = path11.join(
|
|
3501
|
+
const userConfigPath = path11.join(os4.homedir(), ".bluma-cli", "bluma-mcp.json");
|
|
3730
3502
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
3731
3503
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
3732
3504
|
const mergedConfig = {
|
|
@@ -3779,7 +3551,7 @@ var MCPClient = class {
|
|
|
3779
3551
|
async connectToStdioServer(serverName, config2) {
|
|
3780
3552
|
let commandToExecute = config2.command;
|
|
3781
3553
|
let argsToExecute = config2.args || [];
|
|
3782
|
-
const isWindows =
|
|
3554
|
+
const isWindows = os4.platform() === "win32";
|
|
3783
3555
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
3784
3556
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
3785
3557
|
commandToExecute = argsToExecute[1];
|
|
@@ -3899,7 +3671,7 @@ import path14 from "path";
|
|
|
3899
3671
|
|
|
3900
3672
|
// src/app/agent/session_manager/session_manager.ts
|
|
3901
3673
|
import path12 from "path";
|
|
3902
|
-
import
|
|
3674
|
+
import os5 from "os";
|
|
3903
3675
|
import { promises as fs10 } from "fs";
|
|
3904
3676
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
3905
3677
|
async function withFileLock(file, fn) {
|
|
@@ -3915,15 +3687,32 @@ async function withFileLock(file, fn) {
|
|
|
3915
3687
|
if (fileLocks.get(file) === p) fileLocks.delete(file);
|
|
3916
3688
|
}
|
|
3917
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
|
+
}
|
|
3918
3707
|
function expandHome(p) {
|
|
3919
3708
|
if (!p) return p;
|
|
3920
3709
|
if (p.startsWith("~")) {
|
|
3921
|
-
return path12.join(
|
|
3710
|
+
return path12.join(os5.homedir(), p.slice(1));
|
|
3922
3711
|
}
|
|
3923
3712
|
return p;
|
|
3924
3713
|
}
|
|
3925
3714
|
function getPreferredAppDir() {
|
|
3926
|
-
const fixed = path12.join(
|
|
3715
|
+
const fixed = path12.join(os5.homedir(), ".bluma-cli");
|
|
3927
3716
|
return path12.resolve(expandHome(fixed));
|
|
3928
3717
|
}
|
|
3929
3718
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
@@ -3932,25 +3721,36 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
3932
3721
|
const isWin = process.platform === "win32";
|
|
3933
3722
|
while (attempt <= maxRetries) {
|
|
3934
3723
|
try {
|
|
3724
|
+
const dir = path12.dirname(dest);
|
|
3725
|
+
await fs10.mkdir(dir, { recursive: true }).catch(() => {
|
|
3726
|
+
});
|
|
3935
3727
|
await fs10.rename(src, dest);
|
|
3936
3728
|
return;
|
|
3937
3729
|
} catch (e) {
|
|
3938
3730
|
lastErr = e;
|
|
3939
3731
|
const code = e && e.code || "";
|
|
3940
|
-
const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES";
|
|
3941
|
-
if (!
|
|
3732
|
+
const transient = code === "EPERM" || code === "EBUSY" || code === "ENOTEMPTY" || code === "EACCES" || code === "ENOENT";
|
|
3733
|
+
if (!transient || attempt === maxRetries) break;
|
|
3942
3734
|
const backoff = Math.min(1e3, 50 * Math.pow(2, attempt));
|
|
3943
3735
|
await new Promise((r) => setTimeout(r, backoff));
|
|
3944
3736
|
attempt++;
|
|
3945
3737
|
}
|
|
3946
3738
|
}
|
|
3947
3739
|
try {
|
|
3740
|
+
await fs10.access(src);
|
|
3948
3741
|
const data = await fs10.readFile(src);
|
|
3742
|
+
const dir = path12.dirname(dest);
|
|
3743
|
+
await fs10.mkdir(dir, { recursive: true }).catch(() => {
|
|
3744
|
+
});
|
|
3949
3745
|
await fs10.writeFile(dest, data);
|
|
3950
3746
|
await fs10.unlink(src).catch(() => {
|
|
3951
3747
|
});
|
|
3952
3748
|
return;
|
|
3953
3749
|
} catch (fallbackErr) {
|
|
3750
|
+
if (fallbackErr?.code === "ENOENT") {
|
|
3751
|
+
console.warn(`Session temp file disappeared: ${src}`);
|
|
3752
|
+
return;
|
|
3753
|
+
}
|
|
3954
3754
|
throw lastErr || fallbackErr;
|
|
3955
3755
|
}
|
|
3956
3756
|
}
|
|
@@ -3978,7 +3778,7 @@ async function loadOrcreateSession(sessionId2) {
|
|
|
3978
3778
|
return [sessionFile, [], []];
|
|
3979
3779
|
}
|
|
3980
3780
|
}
|
|
3981
|
-
async function
|
|
3781
|
+
async function doSaveSessionHistory(sessionFile, history) {
|
|
3982
3782
|
await withFileLock(sessionFile, async () => {
|
|
3983
3783
|
let sessionData;
|
|
3984
3784
|
try {
|
|
@@ -4028,9 +3828,12 @@ async function saveSessionHistory(sessionFile, history) {
|
|
|
4028
3828
|
}
|
|
4029
3829
|
});
|
|
4030
3830
|
}
|
|
3831
|
+
async function saveSessionHistory(sessionFile, history) {
|
|
3832
|
+
debouncedSave(sessionFile, history);
|
|
3833
|
+
}
|
|
4031
3834
|
|
|
4032
3835
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
4033
|
-
import
|
|
3836
|
+
import os6 from "os";
|
|
4034
3837
|
import fs11 from "fs";
|
|
4035
3838
|
import path13 from "path";
|
|
4036
3839
|
import { execSync } from "child_process";
|
|
@@ -4225,12 +4028,6 @@ You are a **teammate who writes tests**, not an assistant who skips them.
|
|
|
4225
4028
|
- **For edit_tool**: Provide exact content with correct whitespace (read first!)
|
|
4226
4029
|
- **Check file exists** before attempting edits
|
|
4227
4030
|
|
|
4228
|
-
### Shell Commands:
|
|
4229
|
-
- **NEVER use sudo** - Commands requiring sudo will be blocked
|
|
4230
|
-
- **Long commands**: Set appropriate timeout (default 300s)
|
|
4231
|
-
- **Interactive commands** (vim, less, ssh) are blocked - use alternatives
|
|
4232
|
-
- **Large outputs** will be truncated at 100KB
|
|
4233
|
-
|
|
4234
4031
|
### Safe Auto-Approved Tools (no confirmation needed):
|
|
4235
4032
|
- message_notify_user
|
|
4236
4033
|
- ls_tool
|
|
@@ -4246,6 +4043,97 @@ You are a **teammate who writes tests**, not an assistant who skips them.
|
|
|
4246
4043
|
|
|
4247
4044
|
---
|
|
4248
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: 300 })
|
|
4111
|
+
\u2192 { status: "completed", exit_code: 0, truncated: true, stdout: "..." }
|
|
4112
|
+
\`\`\`
|
|
4113
|
+
|
|
4114
|
+
**[WRONG] Never forget command_status:**
|
|
4115
|
+
\`\`\`
|
|
4116
|
+
shell_command({ command: "npm install" })
|
|
4117
|
+
message_notify_user("Installed!") // WRONG: You don't know if it succeeded!
|
|
4118
|
+
\`\`\`
|
|
4119
|
+
|
|
4120
|
+
**[WRONG] Never use blocked commands:**
|
|
4121
|
+
\`\`\`
|
|
4122
|
+
shell_command({ command: "sudo apt install ..." }) // BLOCKED
|
|
4123
|
+
shell_command({ command: "rm -rf /" }) // BLOCKED
|
|
4124
|
+
shell_command({ command: "vim file.txt" }) // BLOCKED (interactive)
|
|
4125
|
+
\`\`\`
|
|
4126
|
+
|
|
4127
|
+
### Tips:
|
|
4128
|
+
- Always check \`exit_code\` - 0 means success, non-zero is error
|
|
4129
|
+
- If \`status: "running"\`, call command_status again with longer wait
|
|
4130
|
+
- If \`truncated: true\`, the output was too long - use \`| head\` or \`| tail\`
|
|
4131
|
+
- Use \`send_command_input\` for commands that need input (y/n prompts)
|
|
4132
|
+
- Use \`kill_command\` to stop a stuck command
|
|
4133
|
+
</shell_command>
|
|
4134
|
+
|
|
4135
|
+
---
|
|
4136
|
+
|
|
4249
4137
|
<communication_style>
|
|
4250
4138
|
## How You Communicate
|
|
4251
4139
|
|
|
@@ -4337,12 +4225,12 @@ Let's build something great, {username}.
|
|
|
4337
4225
|
function getUnifiedSystemPrompt() {
|
|
4338
4226
|
const cwd = process.cwd();
|
|
4339
4227
|
const env = {
|
|
4340
|
-
os_type:
|
|
4341
|
-
os_version:
|
|
4342
|
-
architecture:
|
|
4228
|
+
os_type: os6.type(),
|
|
4229
|
+
os_version: os6.release(),
|
|
4230
|
+
architecture: os6.arch(),
|
|
4343
4231
|
workdir: cwd,
|
|
4344
4232
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
4345
|
-
username:
|
|
4233
|
+
username: os6.userInfo().username,
|
|
4346
4234
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
4347
4235
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
4348
4236
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -4728,7 +4616,7 @@ function getSubAgentByCommand(cmd) {
|
|
|
4728
4616
|
}
|
|
4729
4617
|
|
|
4730
4618
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
4731
|
-
import
|
|
4619
|
+
import os7 from "os";
|
|
4732
4620
|
var SYSTEM_PROMPT2 = `
|
|
4733
4621
|
|
|
4734
4622
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -4891,12 +4779,12 @@ Rule Summary:
|
|
|
4891
4779
|
function getInitPrompt() {
|
|
4892
4780
|
const now = /* @__PURE__ */ new Date();
|
|
4893
4781
|
const collectedData = {
|
|
4894
|
-
os_type:
|
|
4895
|
-
os_version:
|
|
4896
|
-
architecture:
|
|
4782
|
+
os_type: os7.type(),
|
|
4783
|
+
os_version: os7.release(),
|
|
4784
|
+
architecture: os7.arch(),
|
|
4897
4785
|
workdir: process.cwd(),
|
|
4898
4786
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
4899
|
-
username:
|
|
4787
|
+
username: os7.userInfo().username || "Unknown",
|
|
4900
4788
|
current_date: now.toISOString().split("T")[0],
|
|
4901
4789
|
// Formato YYYY-MM-DD
|
|
4902
4790
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -5149,7 +5037,7 @@ var SubAgentsBluMa = class {
|
|
|
5149
5037
|
};
|
|
5150
5038
|
|
|
5151
5039
|
// src/app/agent/agent.ts
|
|
5152
|
-
var globalEnvPath = path15.join(
|
|
5040
|
+
var globalEnvPath = path15.join(os8.homedir(), ".bluma-cli", ".env");
|
|
5153
5041
|
dotenv.config({ path: globalEnvPath });
|
|
5154
5042
|
var Agent = class {
|
|
5155
5043
|
sessionId;
|
|
@@ -5527,18 +5415,6 @@ var renderViewFileOutline = ({ args }) => {
|
|
|
5527
5415
|
] })
|
|
5528
5416
|
] });
|
|
5529
5417
|
};
|
|
5530
|
-
var renderRunCommandAsync = ({ args }) => {
|
|
5531
|
-
const parsed = parseArgs(args);
|
|
5532
|
-
const command = parsed.command || "[no command]";
|
|
5533
|
-
return /* @__PURE__ */ jsxs8(Box8, { paddingX: 1, children: [
|
|
5534
|
-
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "async" }),
|
|
5535
|
-
/* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: " $" }),
|
|
5536
|
-
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
5537
|
-
" ",
|
|
5538
|
-
truncate(command, 50)
|
|
5539
|
-
] })
|
|
5540
|
-
] });
|
|
5541
|
-
};
|
|
5542
5418
|
var renderCommandStatus = ({ args }) => {
|
|
5543
5419
|
const parsed = parseArgs(args);
|
|
5544
5420
|
const id = parsed.command_id || "[no id]";
|
|
@@ -5614,7 +5490,6 @@ var ToolRenderDisplay = {
|
|
|
5614
5490
|
find_by_name: renderFindByName,
|
|
5615
5491
|
grep_search: renderGrepSearch,
|
|
5616
5492
|
view_file_outline: renderViewFileOutline,
|
|
5617
|
-
run_command_async: renderRunCommandAsync,
|
|
5618
5493
|
command_status: renderCommandStatus,
|
|
5619
5494
|
task_boundary: renderTaskBoundary,
|
|
5620
5495
|
search_web: renderSearchWeb
|
|
@@ -5844,10 +5719,20 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
5844
5719
|
] })
|
|
5845
5720
|
] });
|
|
5846
5721
|
}
|
|
5847
|
-
if ((toolName.includes("shell_command") || toolName.includes("run_command")) && parsed) {
|
|
5722
|
+
if ((toolName.includes("shell_command") || toolName.includes("run_command") || toolName.includes("command_status")) && parsed) {
|
|
5848
5723
|
const output = parsed.stdout || parsed.output || "";
|
|
5849
5724
|
const stderr = parsed.stderr || "";
|
|
5850
5725
|
const exitCode = parsed.exit_code ?? parsed.exitCode ?? 0;
|
|
5726
|
+
const status = parsed.status || "";
|
|
5727
|
+
if (parsed.command_id && !output && !stderr && !status) {
|
|
5728
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsxs10(Text10, { dimColor: true, children: [
|
|
5729
|
+
"started #",
|
|
5730
|
+
parsed.command_id.slice(0, 8)
|
|
5731
|
+
] }) });
|
|
5732
|
+
}
|
|
5733
|
+
if (status === "running") {
|
|
5734
|
+
return /* @__PURE__ */ jsx11(Box11, { paddingLeft: 3, children: /* @__PURE__ */ jsx11(Text10, { dimColor: true, color: "yellow", children: "still running..." }) });
|
|
5735
|
+
}
|
|
5851
5736
|
if (!output && !stderr) return null;
|
|
5852
5737
|
const { lines, truncated } = truncateLines(output || stderr, MAX_LINES);
|
|
5853
5738
|
const isError = exitCode !== 0 || stderr;
|
|
@@ -5857,6 +5742,10 @@ var ToolResultDisplayComponent = ({ toolName, result }) => {
|
|
|
5857
5742
|
"... +",
|
|
5858
5743
|
truncated,
|
|
5859
5744
|
" lines"
|
|
5745
|
+
] }),
|
|
5746
|
+
exitCode !== 0 && /* @__PURE__ */ jsxs10(Text10, { dimColor: true, color: "red", children: [
|
|
5747
|
+
"exit code: ",
|
|
5748
|
+
exitCode
|
|
5860
5749
|
] })
|
|
5861
5750
|
] });
|
|
5862
5751
|
}
|