@nomad-e/bluma-cli 0.1.62 → 0.1.64
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/main.js +1463 -304
- package/package.json +6 -1
package/dist/main.js
CHANGED
|
@@ -319,35 +319,13 @@ var init_sandbox_policy = __esm({
|
|
|
319
319
|
init_runtime_config();
|
|
320
320
|
init_permission_rules();
|
|
321
321
|
BLOCKED_COMMAND_PATTERNS = [
|
|
322
|
-
|
|
323
|
-
{ pattern: /^doas\s+/i, reason: "Privilege escalation is not allowed." },
|
|
324
|
-
{ pattern: /^su\s+/i, reason: "Privilege escalation is not allowed." },
|
|
325
|
-
{ pattern: /^pkexec\s+/i, reason: "Privilege escalation is not allowed." },
|
|
326
|
-
{ pattern: /\bmkfs\./i, reason: "Disk formatting commands are blocked." },
|
|
327
|
-
{ pattern: /\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i, reason: "Raw disk writes are blocked." },
|
|
328
|
-
{ pattern: /\brm\s+(-[rf]+\s+)*\/($|\s)/i, reason: "Deleting filesystem roots is blocked." },
|
|
329
|
-
{ pattern: /\brm\s+-[rf]*\s+~($|\s)/i, reason: "Deleting home roots is blocked." },
|
|
330
|
-
{ pattern: /\bcurl\s+.*\|\s*(ba)?sh/i, reason: "Pipe-to-shell remote execution is blocked." },
|
|
331
|
-
{ pattern: /\bwget\s+.*\|\s*(ba)?sh/i, reason: "Pipe-to-shell remote execution is blocked." }
|
|
322
|
+
// No command patterns blocked — sandbox isolation handles safety
|
|
332
323
|
];
|
|
333
324
|
HIGH_RISK_COMMAND_PATTERNS = [
|
|
334
|
-
|
|
335
|
-
/\bmv\s+.+\s+\/(?!tmp\b)/i,
|
|
336
|
-
/\bchmod\b/i,
|
|
337
|
-
/\bchown\b/i,
|
|
338
|
-
/\bssh\b/i,
|
|
339
|
-
/\bscp\b/i,
|
|
340
|
-
/\brsync\b/i,
|
|
341
|
-
/\bdocker\b/i,
|
|
342
|
-
/\bkubectl\b/i
|
|
325
|
+
// No high-risk patterns — all commands allowed inside sandbox
|
|
343
326
|
];
|
|
344
327
|
MODERATE_RISK_COMMAND_PATTERNS = [
|
|
345
|
-
|
|
346
|
-
/\bpnpm\s+(add|install|update|remove)\b/i,
|
|
347
|
-
/\byarn\s+(add|install|remove)\b/i,
|
|
348
|
-
/\buv\s+(add|remove|sync)\b/i,
|
|
349
|
-
/\bpip\s+install\b/i,
|
|
350
|
-
/\bgit\s+(commit|push|rebase|reset|clean)\b/i
|
|
328
|
+
// No moderate-risk patterns — all commands allowed inside sandbox
|
|
351
329
|
];
|
|
352
330
|
}
|
|
353
331
|
});
|
|
@@ -1742,10 +1720,10 @@ var init_themes = __esm({
|
|
|
1742
1720
|
import React19 from "react";
|
|
1743
1721
|
import { render } from "ink";
|
|
1744
1722
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
1745
|
-
import
|
|
1746
|
-
import
|
|
1723
|
+
import fs37 from "fs";
|
|
1724
|
+
import path42 from "path";
|
|
1747
1725
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1748
|
-
import { spawn as
|
|
1726
|
+
import { spawn as spawn6 } from "child_process";
|
|
1749
1727
|
import { v4 as uuidv412 } from "uuid";
|
|
1750
1728
|
|
|
1751
1729
|
// src/app/ui/App.tsx
|
|
@@ -2554,7 +2532,7 @@ var filterSlashCommands = (query) => {
|
|
|
2554
2532
|
let tier = 3;
|
|
2555
2533
|
let scorePrimary = 0;
|
|
2556
2534
|
let scoreSecondary = Number.MAX_SAFE_INTEGER;
|
|
2557
|
-
|
|
2535
|
+
const scoreTertiary = name.length;
|
|
2558
2536
|
if (isPrefix) {
|
|
2559
2537
|
tier = 0;
|
|
2560
2538
|
scorePrimary = q.length * -1;
|
|
@@ -4292,12 +4270,12 @@ function EditToolDiffPanel({
|
|
|
4292
4270
|
maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
|
|
4293
4271
|
fallbackSnippet
|
|
4294
4272
|
}) {
|
|
4295
|
-
const
|
|
4273
|
+
const path43 = filePath.trim() || "unknown file";
|
|
4296
4274
|
const diff = diffText?.trim() ?? "";
|
|
4297
4275
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
4298
4276
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexWrap: "wrap", children: /* @__PURE__ */ jsxs5(Text5, { color: isNewFile ? BLUMA_TERMINAL.success : void 0, children: [
|
|
4299
4277
|
isNewFile ? "Created " : "Wrote to ",
|
|
4300
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children:
|
|
4278
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: path43 })
|
|
4301
4279
|
] }) }),
|
|
4302
4280
|
description ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: description }) : null,
|
|
4303
4281
|
diff.length > 0 ? /* @__PURE__ */ jsx5(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx5(SimpleDiff, { text: diff, maxHeight, frame: true }) }) : fallbackSnippet ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 0, children: [
|
|
@@ -4626,7 +4604,7 @@ var renderFindByName = ({ args }) => {
|
|
|
4626
4604
|
var renderGrepSearch = ({ args }) => {
|
|
4627
4605
|
const parsed = parseArgs(args);
|
|
4628
4606
|
const query = parsed.query || "";
|
|
4629
|
-
const
|
|
4607
|
+
const path43 = parsed.path || ".";
|
|
4630
4608
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
4631
4609
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.muted, children: [
|
|
4632
4610
|
'"',
|
|
@@ -4635,7 +4613,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
4635
4613
|
] }),
|
|
4636
4614
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: [
|
|
4637
4615
|
" ",
|
|
4638
|
-
|
|
4616
|
+
path43
|
|
4639
4617
|
] })
|
|
4640
4618
|
] });
|
|
4641
4619
|
};
|
|
@@ -5061,12 +5039,12 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
5061
5039
|
|
|
5062
5040
|
// src/app/agent/agent.ts
|
|
5063
5041
|
import * as dotenv from "dotenv";
|
|
5064
|
-
import
|
|
5065
|
-
import
|
|
5042
|
+
import path40 from "path";
|
|
5043
|
+
import os27 from "os";
|
|
5066
5044
|
|
|
5067
5045
|
// src/app/agent/tool_invoker.ts
|
|
5068
|
-
import { promises as
|
|
5069
|
-
import
|
|
5046
|
+
import { promises as fs25 } from "fs";
|
|
5047
|
+
import path27 from "path";
|
|
5070
5048
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5071
5049
|
|
|
5072
5050
|
// src/app/agent/tools/natives/edit.ts
|
|
@@ -5121,9 +5099,9 @@ function countOccurrences(text, search) {
|
|
|
5121
5099
|
return text.split(search).length - 1;
|
|
5122
5100
|
}
|
|
5123
5101
|
function ensureCorrectEdit(currentContent, originalOldString, originalNewString, expectedReplacements) {
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5102
|
+
const finalOldString = originalOldString;
|
|
5103
|
+
const finalNewString = originalNewString;
|
|
5104
|
+
const occurrences = countOccurrences(currentContent, finalOldString);
|
|
5127
5105
|
if (occurrences > 0) {
|
|
5128
5106
|
return [finalOldString, finalNewString, occurrences];
|
|
5129
5107
|
}
|
|
@@ -5280,7 +5258,7 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
|
|
|
5280
5258
|
};
|
|
5281
5259
|
}
|
|
5282
5260
|
let currentContent = null;
|
|
5283
|
-
|
|
5261
|
+
const isNewFile = false;
|
|
5284
5262
|
try {
|
|
5285
5263
|
const stats = await fs6.stat(normalizedFilePath);
|
|
5286
5264
|
if (stats.size > MAX_FILE_SIZE) {
|
|
@@ -5728,7 +5706,7 @@ async function readLines(args) {
|
|
|
5728
5706
|
if (startIndex >= total_lines) {
|
|
5729
5707
|
throw new Error(`start_line (${startLine}) exceeds file length (${total_lines} lines).`);
|
|
5730
5708
|
}
|
|
5731
|
-
|
|
5709
|
+
const endIndex = Math.min(endLine, total_lines);
|
|
5732
5710
|
const contentLines = lines.slice(startIndex, endIndex);
|
|
5733
5711
|
let content = contentLines.join("\n");
|
|
5734
5712
|
if (args.line_number_prefix === true) {
|
|
@@ -6050,7 +6028,7 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
6050
6028
|
var MAX_RESULTS2 = 100;
|
|
6051
6029
|
var MAX_DEPTH_DEFAULT = 10;
|
|
6052
6030
|
function globToRegex(glob) {
|
|
6053
|
-
|
|
6031
|
+
const regex = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{DOUBLESTAR}}/g, ".*");
|
|
6054
6032
|
return new RegExp(`^${regex}$`, "i");
|
|
6055
6033
|
}
|
|
6056
6034
|
function shouldIgnore(name, ignorePatterns, includeHidden) {
|
|
@@ -8935,7 +8913,7 @@ async function dream(args = {}) {
|
|
|
8935
8913
|
const used = /* @__PURE__ */ new Set();
|
|
8936
8914
|
for (let i = 0; i < entries.length; i++) {
|
|
8937
8915
|
if (used.has(entries[i].id)) continue;
|
|
8938
|
-
|
|
8916
|
+
const current = { ...entries[i] };
|
|
8939
8917
|
used.add(entries[i].id);
|
|
8940
8918
|
for (let j = i + 1; j < entries.length; j++) {
|
|
8941
8919
|
if (used.has(entries[j].id)) continue;
|
|
@@ -9210,6 +9188,1021 @@ async function context_collapse(args = {}) {
|
|
|
9210
9188
|
|
|
9211
9189
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
9212
9190
|
init_agent_coordination();
|
|
9191
|
+
|
|
9192
|
+
// src/app/agent/tools/natives/create-next-app.ts
|
|
9193
|
+
init_sandbox_policy();
|
|
9194
|
+
import { promises as fs23 } from "fs";
|
|
9195
|
+
import path25 from "path";
|
|
9196
|
+
|
|
9197
|
+
// src/app/agent/tools/natives/shell_command.ts
|
|
9198
|
+
init_sandbox_policy();
|
|
9199
|
+
import os15 from "os";
|
|
9200
|
+
import { spawn as spawn5 } from "child_process";
|
|
9201
|
+
var MAX_OUTPUT_SIZE2 = 3e4;
|
|
9202
|
+
var MAX_OUTPUT_LINES = 200;
|
|
9203
|
+
var OUTPUT_TRUNCATION_MESSAGE = "\n\n[OUTPUT TRUNCATED - use `| head -n X` or redirect to file for full output]";
|
|
9204
|
+
var DANGEROUS_PATTERNS = [
|
|
9205
|
+
// === ELEVAÇÃO DE PRIVILÉGIOS ===
|
|
9206
|
+
/^sudo\s+/i,
|
|
9207
|
+
// sudo commands
|
|
9208
|
+
/^doas\s+/i,
|
|
9209
|
+
// doas (BSD sudo alternative)
|
|
9210
|
+
/^su\s+/i,
|
|
9211
|
+
// switch user
|
|
9212
|
+
/^pkexec\s+/i,
|
|
9213
|
+
// PolicyKit execution
|
|
9214
|
+
/\|\s*sudo\s+/i,
|
|
9215
|
+
// piped to sudo
|
|
9216
|
+
/;\s*sudo\s+/i,
|
|
9217
|
+
// chained with sudo
|
|
9218
|
+
/&&\s*sudo\s+/i,
|
|
9219
|
+
// AND chained with sudo
|
|
9220
|
+
// === COMANDOS DESTRUTIVOS ===
|
|
9221
|
+
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
9222
|
+
// rm com paths perigosos (/, ~)
|
|
9223
|
+
/\brm\s+-[rf]*\s+\*/i,
|
|
9224
|
+
// rm -rf *
|
|
9225
|
+
/\bchmod\s+(777|666)\s+\//i,
|
|
9226
|
+
// chmod 777/666 em paths root
|
|
9227
|
+
/\bchown\s+.*\s+\//i,
|
|
9228
|
+
// chown em paths root
|
|
9229
|
+
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
9230
|
+
// dd para discos
|
|
9231
|
+
/\bmkfs\./i,
|
|
9232
|
+
// format filesystem
|
|
9233
|
+
/>\s*\/dev\/(sd|hd|nvme)/i,
|
|
9234
|
+
// redirect para disco
|
|
9235
|
+
/\bshred\s+/i,
|
|
9236
|
+
// secure delete
|
|
9237
|
+
// === FORK BOMB / RESOURCE EXHAUSTION ===
|
|
9238
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
9239
|
+
// fork bomb clássico
|
|
9240
|
+
/\bwhile\s+true\s*;\s*do/i,
|
|
9241
|
+
// infinite loop
|
|
9242
|
+
/\byes\s+\|/i,
|
|
9243
|
+
// yes piped (resource exhaustion)
|
|
9244
|
+
// === NETWORK PERIGOSO ===
|
|
9245
|
+
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
9246
|
+
// curl | bash (remote code exec)
|
|
9247
|
+
/\bwget\s+.*\|\s*(ba)?sh/i,
|
|
9248
|
+
// wget | bash
|
|
9249
|
+
/\bnc\s+-[el]/i
|
|
9250
|
+
// netcat listener (backdoor)
|
|
9251
|
+
];
|
|
9252
|
+
var INTERACTIVE_PATTERNS2 = [
|
|
9253
|
+
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
9254
|
+
// editors
|
|
9255
|
+
/^(less|more|most)\s*/i,
|
|
9256
|
+
// pagers
|
|
9257
|
+
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
9258
|
+
// monitoring tools
|
|
9259
|
+
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
9260
|
+
// remote connections
|
|
9261
|
+
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
9262
|
+
// database CLIs (sem script)
|
|
9263
|
+
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
9264
|
+
// REPLs sem script
|
|
9265
|
+
/^(gdb|lldb)\s*/i,
|
|
9266
|
+
// debuggers
|
|
9267
|
+
/^(bc|dc)\s*$/i
|
|
9268
|
+
// calculators
|
|
9269
|
+
];
|
|
9270
|
+
function shellCommand(args) {
|
|
9271
|
+
const {
|
|
9272
|
+
command,
|
|
9273
|
+
timeout = 300,
|
|
9274
|
+
// 5 minutos por padrão
|
|
9275
|
+
cwd = process.cwd(),
|
|
9276
|
+
verbose = false
|
|
9277
|
+
} = args;
|
|
9278
|
+
return new Promise((resolve2) => {
|
|
9279
|
+
const startTime = Date.now();
|
|
9280
|
+
const platform = os15.platform();
|
|
9281
|
+
const commandTrimmed = command.trim();
|
|
9282
|
+
let resolvedCwd;
|
|
9283
|
+
try {
|
|
9284
|
+
resolvedCwd = resolveCommandCwd(cwd);
|
|
9285
|
+
} catch (err) {
|
|
9286
|
+
const result = {
|
|
9287
|
+
status: "blocked",
|
|
9288
|
+
exitCode: null,
|
|
9289
|
+
stdout: "",
|
|
9290
|
+
stderr: `Command blocked: ${err.message}`,
|
|
9291
|
+
command,
|
|
9292
|
+
cwd,
|
|
9293
|
+
platform,
|
|
9294
|
+
duration: Date.now() - startTime,
|
|
9295
|
+
blockedReason: "sandbox_path_violation"
|
|
9296
|
+
};
|
|
9297
|
+
return resolve2(formatResult(result, verbose));
|
|
9298
|
+
}
|
|
9299
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
9300
|
+
if (pattern.test(commandTrimmed)) {
|
|
9301
|
+
const result = {
|
|
9302
|
+
status: "blocked",
|
|
9303
|
+
exitCode: null,
|
|
9304
|
+
stdout: "",
|
|
9305
|
+
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
9306
|
+
1. Run the command manually in terminal
|
|
9307
|
+
2. Configure passwordless sudo for this specific command
|
|
9308
|
+
3. Run BluMa as root (not recommended)`,
|
|
9309
|
+
command,
|
|
9310
|
+
cwd,
|
|
9311
|
+
platform,
|
|
9312
|
+
duration: Date.now() - startTime,
|
|
9313
|
+
blockedReason: "requires_sudo"
|
|
9314
|
+
};
|
|
9315
|
+
return resolve2(formatResult(result, verbose));
|
|
9316
|
+
}
|
|
9317
|
+
}
|
|
9318
|
+
for (const pattern of INTERACTIVE_PATTERNS2) {
|
|
9319
|
+
if (pattern.test(commandTrimmed)) {
|
|
9320
|
+
const result = {
|
|
9321
|
+
status: "blocked",
|
|
9322
|
+
exitCode: null,
|
|
9323
|
+
stdout: "",
|
|
9324
|
+
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
9325
|
+
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
9326
|
+
2. Run the command manually in terminal
|
|
9327
|
+
3. For editors, use edit_tool instead`,
|
|
9328
|
+
command,
|
|
9329
|
+
cwd,
|
|
9330
|
+
platform,
|
|
9331
|
+
duration: Date.now() - startTime,
|
|
9332
|
+
blockedReason: "interactive_command"
|
|
9333
|
+
};
|
|
9334
|
+
return resolve2(formatResult(result, verbose));
|
|
9335
|
+
}
|
|
9336
|
+
}
|
|
9337
|
+
let shellCmd;
|
|
9338
|
+
let shellArgs;
|
|
9339
|
+
if (platform === "win32") {
|
|
9340
|
+
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
9341
|
+
shellArgs = ["/c", command];
|
|
9342
|
+
} else {
|
|
9343
|
+
shellCmd = process.env.SHELL || "/bin/bash";
|
|
9344
|
+
shellArgs = ["-c", command];
|
|
9345
|
+
}
|
|
9346
|
+
let stdout = "";
|
|
9347
|
+
let stderr = "";
|
|
9348
|
+
let stdoutTruncated = false;
|
|
9349
|
+
let stderrTruncated = false;
|
|
9350
|
+
let timedOut = false;
|
|
9351
|
+
let finished = false;
|
|
9352
|
+
const childProcess = spawn5(shellCmd, shellArgs, {
|
|
9353
|
+
cwd: resolvedCwd,
|
|
9354
|
+
env: process.env,
|
|
9355
|
+
// Importante: no Windows, precisamos do shell, mas spawn já lida com isso
|
|
9356
|
+
windowsHide: true
|
|
9357
|
+
});
|
|
9358
|
+
const timeoutId = setTimeout(() => {
|
|
9359
|
+
if (!finished) {
|
|
9360
|
+
timedOut = true;
|
|
9361
|
+
childProcess.kill("SIGTERM");
|
|
9362
|
+
setTimeout(() => {
|
|
9363
|
+
if (!finished) {
|
|
9364
|
+
childProcess.kill("SIGKILL");
|
|
9365
|
+
}
|
|
9366
|
+
}, 2e3);
|
|
9367
|
+
}
|
|
9368
|
+
}, timeout * 1e3);
|
|
9369
|
+
if (childProcess.stdout) {
|
|
9370
|
+
childProcess.stdout.on("data", (data) => {
|
|
9371
|
+
if (!stdoutTruncated) {
|
|
9372
|
+
stdout += data.toString();
|
|
9373
|
+
if (stdout.length > MAX_OUTPUT_SIZE2) {
|
|
9374
|
+
stdout = stdout.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MESSAGE;
|
|
9375
|
+
stdoutTruncated = true;
|
|
9376
|
+
}
|
|
9377
|
+
}
|
|
9378
|
+
});
|
|
9379
|
+
}
|
|
9380
|
+
if (childProcess.stderr) {
|
|
9381
|
+
childProcess.stderr.on("data", (data) => {
|
|
9382
|
+
if (!stderrTruncated) {
|
|
9383
|
+
stderr += data.toString();
|
|
9384
|
+
if (stderr.length > MAX_OUTPUT_SIZE2) {
|
|
9385
|
+
stderr = stderr.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MESSAGE;
|
|
9386
|
+
stderrTruncated = true;
|
|
9387
|
+
}
|
|
9388
|
+
}
|
|
9389
|
+
});
|
|
9390
|
+
}
|
|
9391
|
+
childProcess.on("error", (error) => {
|
|
9392
|
+
if (!finished) {
|
|
9393
|
+
finished = true;
|
|
9394
|
+
clearTimeout(timeoutId);
|
|
9395
|
+
const result = {
|
|
9396
|
+
status: "error",
|
|
9397
|
+
exitCode: null,
|
|
9398
|
+
stdout: stdout.trim(),
|
|
9399
|
+
stderr: `Failed to execute command: ${error.message}`,
|
|
9400
|
+
command,
|
|
9401
|
+
cwd,
|
|
9402
|
+
platform,
|
|
9403
|
+
duration: Date.now() - startTime
|
|
9404
|
+
};
|
|
9405
|
+
resolve2(formatResult(result, verbose));
|
|
9406
|
+
}
|
|
9407
|
+
});
|
|
9408
|
+
childProcess.on("close", (code, signal) => {
|
|
9409
|
+
if (!finished) {
|
|
9410
|
+
finished = true;
|
|
9411
|
+
clearTimeout(timeoutId);
|
|
9412
|
+
const result = {
|
|
9413
|
+
status: timedOut ? "timeout" : code === 0 ? "success" : "error",
|
|
9414
|
+
exitCode: code,
|
|
9415
|
+
stdout: stdout.trim(),
|
|
9416
|
+
stderr: timedOut ? `Command timed out after ${timeout} seconds
|
|
9417
|
+
${stderr.trim()}` : stderr.trim(),
|
|
9418
|
+
command,
|
|
9419
|
+
cwd,
|
|
9420
|
+
platform,
|
|
9421
|
+
duration: Date.now() - startTime,
|
|
9422
|
+
truncated: stdoutTruncated || stderrTruncated
|
|
9423
|
+
};
|
|
9424
|
+
resolve2(formatResult(result, verbose));
|
|
9425
|
+
}
|
|
9426
|
+
});
|
|
9427
|
+
});
|
|
9428
|
+
}
|
|
9429
|
+
function smartTruncate(text) {
|
|
9430
|
+
if (!text) return { result: "", wasTruncated: false };
|
|
9431
|
+
let result = text;
|
|
9432
|
+
let wasTruncated = false;
|
|
9433
|
+
if (result.length > MAX_OUTPUT_SIZE2) {
|
|
9434
|
+
result = result.substring(0, MAX_OUTPUT_SIZE2);
|
|
9435
|
+
wasTruncated = true;
|
|
9436
|
+
}
|
|
9437
|
+
const lines = result.split("\n");
|
|
9438
|
+
if (lines.length > MAX_OUTPUT_LINES) {
|
|
9439
|
+
const head = lines.slice(0, 50);
|
|
9440
|
+
const tail = lines.slice(-50);
|
|
9441
|
+
result = head.join("\n") + `
|
|
9442
|
+
|
|
9443
|
+
... [${lines.length - 100} lines omitted] ...
|
|
9444
|
+
|
|
9445
|
+
` + tail.join("\n");
|
|
9446
|
+
wasTruncated = true;
|
|
9447
|
+
}
|
|
9448
|
+
return { result, wasTruncated };
|
|
9449
|
+
}
|
|
9450
|
+
function formatResult(result, verbose) {
|
|
9451
|
+
const stdout = smartTruncate(result.stdout);
|
|
9452
|
+
const stderr = smartTruncate(result.stderr);
|
|
9453
|
+
const wasTruncated = result.truncated || stdout.wasTruncated || stderr.wasTruncated;
|
|
9454
|
+
if (verbose) {
|
|
9455
|
+
return JSON.stringify({
|
|
9456
|
+
...result,
|
|
9457
|
+
stdout: stdout.result,
|
|
9458
|
+
stderr: stderr.result,
|
|
9459
|
+
truncated: wasTruncated
|
|
9460
|
+
}, null, 2);
|
|
9461
|
+
}
|
|
9462
|
+
const output = {
|
|
9463
|
+
status: result.status,
|
|
9464
|
+
exitCode: result.exitCode
|
|
9465
|
+
};
|
|
9466
|
+
if (stdout.result) {
|
|
9467
|
+
output.stdout = stdout.result;
|
|
9468
|
+
}
|
|
9469
|
+
if (stderr.result) {
|
|
9470
|
+
output.stderr = stderr.result;
|
|
9471
|
+
}
|
|
9472
|
+
if (result.status === "timeout") {
|
|
9473
|
+
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
9474
|
+
}
|
|
9475
|
+
if (result.status === "blocked" && result.blockedReason) {
|
|
9476
|
+
output.blockedReason = result.blockedReason;
|
|
9477
|
+
}
|
|
9478
|
+
if (wasTruncated) {
|
|
9479
|
+
output.warning = "Output truncated (30KB / 200 lines max). Use `| head -n X` or redirect to file.";
|
|
9480
|
+
}
|
|
9481
|
+
return JSON.stringify(output, null, 2);
|
|
9482
|
+
}
|
|
9483
|
+
|
|
9484
|
+
// src/app/agent/tools/natives/create-next-app.ts
|
|
9485
|
+
var MINIMAL_FILES = {
|
|
9486
|
+
"package.json": `{
|
|
9487
|
+
"name": "{{NAME}}",
|
|
9488
|
+
"version": "0.1.0",
|
|
9489
|
+
"private": true,
|
|
9490
|
+
"scripts": {
|
|
9491
|
+
"dev": "next dev",
|
|
9492
|
+
"build": "next build",
|
|
9493
|
+
"start": "next start",
|
|
9494
|
+
"lint": "next lint"
|
|
9495
|
+
},
|
|
9496
|
+
"dependencies": {
|
|
9497
|
+
"next": "^14.1.0",
|
|
9498
|
+
"react": "^18.2.0",
|
|
9499
|
+
"react-dom": "^18.2.0"
|
|
9500
|
+
},
|
|
9501
|
+
"devDependencies": {
|
|
9502
|
+
"@types/node": "^20.11.0",
|
|
9503
|
+
"@types/react": "^18.2.0",
|
|
9504
|
+
"@types/react-dom": "^18.2.0",
|
|
9505
|
+
"typescript": "^5.3.0",
|
|
9506
|
+
"tailwindcss": "^3.4.0",
|
|
9507
|
+
"postcss": "^8.4.0",
|
|
9508
|
+
"autoprefixer": "^10.4.0",
|
|
9509
|
+
"eslint": "^8.56.0",
|
|
9510
|
+
"eslint-config-next": "^14.1.0"
|
|
9511
|
+
}
|
|
9512
|
+
}
|
|
9513
|
+
`,
|
|
9514
|
+
"tsconfig.json": `{
|
|
9515
|
+
"compilerOptions": {
|
|
9516
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
9517
|
+
"allowJs": true,
|
|
9518
|
+
"skipLibCheck": true,
|
|
9519
|
+
"strict": true,
|
|
9520
|
+
"noEmit": true,
|
|
9521
|
+
"esModuleInterop": true,
|
|
9522
|
+
"module": "esnext",
|
|
9523
|
+
"moduleResolution": "bundler",
|
|
9524
|
+
"resolveJsonModule": true,
|
|
9525
|
+
"isolatedModules": true,
|
|
9526
|
+
"jsx": "preserve",
|
|
9527
|
+
"incremental": true,
|
|
9528
|
+
"plugins": [
|
|
9529
|
+
{
|
|
9530
|
+
"name": "next"
|
|
9531
|
+
}
|
|
9532
|
+
],
|
|
9533
|
+
"paths": {
|
|
9534
|
+
"@/*": ["./*"]
|
|
9535
|
+
}
|
|
9536
|
+
},
|
|
9537
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
9538
|
+
"exclude": ["node_modules"]
|
|
9539
|
+
}
|
|
9540
|
+
`,
|
|
9541
|
+
"next.config.js": `/** @type {import('next').NextConfig} */
|
|
9542
|
+
const nextConfig = {
|
|
9543
|
+
output: 'standalone',
|
|
9544
|
+
reactStrictMode: true,
|
|
9545
|
+
}
|
|
9546
|
+
|
|
9547
|
+
module.exports = nextConfig
|
|
9548
|
+
`,
|
|
9549
|
+
"tailwind.config.ts": `import type { Config } from 'tailwindcss'
|
|
9550
|
+
|
|
9551
|
+
const config: Config = {
|
|
9552
|
+
content: [
|
|
9553
|
+
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9554
|
+
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9555
|
+
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9556
|
+
],
|
|
9557
|
+
theme: {
|
|
9558
|
+
extend: {
|
|
9559
|
+
backgroundImage: {
|
|
9560
|
+
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
|
9561
|
+
'gradient-conic':
|
|
9562
|
+
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
|
9563
|
+
},
|
|
9564
|
+
},
|
|
9565
|
+
},
|
|
9566
|
+
plugins: [],
|
|
9567
|
+
}
|
|
9568
|
+
export default config
|
|
9569
|
+
`,
|
|
9570
|
+
"postcss.config.js": `module.exports = {
|
|
9571
|
+
plugins: {
|
|
9572
|
+
tailwindcss: {},
|
|
9573
|
+
autoprefixer: {},
|
|
9574
|
+
},
|
|
9575
|
+
}
|
|
9576
|
+
`,
|
|
9577
|
+
".eslintrc.json": `{
|
|
9578
|
+
"extends": "next/core-web-vitals"
|
|
9579
|
+
}
|
|
9580
|
+
`,
|
|
9581
|
+
".gitignore": `# dependencies
|
|
9582
|
+
/node_modules
|
|
9583
|
+
/.pnp
|
|
9584
|
+
.pnp.js
|
|
9585
|
+
|
|
9586
|
+
# testing
|
|
9587
|
+
/coverage
|
|
9588
|
+
|
|
9589
|
+
# next.js
|
|
9590
|
+
/.next/
|
|
9591
|
+
/out/
|
|
9592
|
+
|
|
9593
|
+
# production
|
|
9594
|
+
/build
|
|
9595
|
+
|
|
9596
|
+
# misc
|
|
9597
|
+
.DS_Store
|
|
9598
|
+
*.pem
|
|
9599
|
+
|
|
9600
|
+
# debug
|
|
9601
|
+
npm-debug.log*
|
|
9602
|
+
yarn-debug.log*
|
|
9603
|
+
yarn-error.log*
|
|
9604
|
+
|
|
9605
|
+
# local env files
|
|
9606
|
+
.env*.local
|
|
9607
|
+
.env
|
|
9608
|
+
|
|
9609
|
+
# typescript
|
|
9610
|
+
*.tsbuildinfo
|
|
9611
|
+
next-env.d.ts
|
|
9612
|
+
`
|
|
9613
|
+
};
|
|
9614
|
+
var FULL_FILES = {
|
|
9615
|
+
...MINIMAL_FILES,
|
|
9616
|
+
// App Router structure
|
|
9617
|
+
"app/layout.tsx": `import type { Metadata } from "next";
|
|
9618
|
+
import { Inter } from "next/font/google";
|
|
9619
|
+
import "./globals.css";
|
|
9620
|
+
|
|
9621
|
+
const inter = Inter({ subsets: ["latin"] });
|
|
9622
|
+
|
|
9623
|
+
export const metadata: Metadata = {
|
|
9624
|
+
title: "{{NAME}}",
|
|
9625
|
+
description: "Generated by create-next-app",
|
|
9626
|
+
};
|
|
9627
|
+
|
|
9628
|
+
export default function RootLayout({
|
|
9629
|
+
children,
|
|
9630
|
+
}: Readonly<{
|
|
9631
|
+
children: React.ReactNode;
|
|
9632
|
+
}>) {
|
|
9633
|
+
return (
|
|
9634
|
+
<html lang="en">
|
|
9635
|
+
<body className={inter.className}>{children}</body>
|
|
9636
|
+
</html>
|
|
9637
|
+
);
|
|
9638
|
+
}
|
|
9639
|
+
`,
|
|
9640
|
+
"app/page.tsx": `export default function Home() {
|
|
9641
|
+
return (
|
|
9642
|
+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
|
9643
|
+
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
|
9644
|
+
<p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
|
|
9645
|
+
Welcome to <code className="font-mono font-bold">{{NAME}}</code>
|
|
9646
|
+
</p>
|
|
9647
|
+
</div>
|
|
9648
|
+
|
|
9649
|
+
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
|
9650
|
+
<a
|
|
9651
|
+
href="https://nextjs.org/docs"
|
|
9652
|
+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
9653
|
+
target="_blank"
|
|
9654
|
+
rel="noopener noreferrer"
|
|
9655
|
+
>
|
|
9656
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9657
|
+
Docs{" "}
|
|
9658
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9659
|
+
->
|
|
9660
|
+
</span>
|
|
9661
|
+
</h2>
|
|
9662
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9663
|
+
Find in-depth information about Next.js features and API.
|
|
9664
|
+
</p>
|
|
9665
|
+
</a>
|
|
9666
|
+
|
|
9667
|
+
<a
|
|
9668
|
+
href="https://nextjs.org/learn"
|
|
9669
|
+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
9670
|
+
target="_blank"
|
|
9671
|
+
rel="noopener noreferrer"
|
|
9672
|
+
>
|
|
9673
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9674
|
+
Learn{" "}
|
|
9675
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9676
|
+
->
|
|
9677
|
+
</span>
|
|
9678
|
+
</h2>
|
|
9679
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9680
|
+
Learn about Next.js in an interactive course with quizzes!
|
|
9681
|
+
</p>
|
|
9682
|
+
</a>
|
|
9683
|
+
|
|
9684
|
+
<a
|
|
9685
|
+
href="https://vercel.com/templates"
|
|
9686
|
+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
9687
|
+
target="_blank"
|
|
9688
|
+
rel="noopener noreferrer"
|
|
9689
|
+
>
|
|
9690
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9691
|
+
Templates{" "}
|
|
9692
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9693
|
+
->
|
|
9694
|
+
</span>
|
|
9695
|
+
</h2>
|
|
9696
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9697
|
+
Explore starter templates for Next.js.
|
|
9698
|
+
</p>
|
|
9699
|
+
</a>
|
|
9700
|
+
|
|
9701
|
+
<a
|
|
9702
|
+
href="https://vercel.com/new"
|
|
9703
|
+
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-gray-300 hover:bg-gray-100 hover:dark:border-neutral-700 hover:dark:bg-neutral-800/30"
|
|
9704
|
+
target="_blank"
|
|
9705
|
+
rel="noopener noreferrer"
|
|
9706
|
+
>
|
|
9707
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9708
|
+
Deploy{" "}
|
|
9709
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9710
|
+
->
|
|
9711
|
+
</span>
|
|
9712
|
+
</h2>
|
|
9713
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50 text-balance\`}>
|
|
9714
|
+
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
|
9715
|
+
</p>
|
|
9716
|
+
</a>
|
|
9717
|
+
</div>
|
|
9718
|
+
</main>
|
|
9719
|
+
);
|
|
9720
|
+
}
|
|
9721
|
+
`,
|
|
9722
|
+
"app/globals.css": `@tailwind base;
|
|
9723
|
+
@tailwind components;
|
|
9724
|
+
@tailwind utilities;
|
|
9725
|
+
|
|
9726
|
+
:root {
|
|
9727
|
+
--foreground-rgb: 0, 0, 0;
|
|
9728
|
+
--background-start-rgb: 214, 219, 220;
|
|
9729
|
+
--background-end-rgb: 255, 255, 255;
|
|
9730
|
+
}
|
|
9731
|
+
|
|
9732
|
+
@media (prefers-color-scheme: dark) {
|
|
9733
|
+
:root {
|
|
9734
|
+
--foreground-rgb: 255, 255, 255;
|
|
9735
|
+
--background-start-rgb: 0, 0, 0;
|
|
9736
|
+
--background-end-rgb: 0, 0, 0;
|
|
9737
|
+
}
|
|
9738
|
+
}
|
|
9739
|
+
|
|
9740
|
+
body {
|
|
9741
|
+
color: rgb(var(--foreground-rgb));
|
|
9742
|
+
background: linear-gradient(
|
|
9743
|
+
to bottom,
|
|
9744
|
+
transparent,
|
|
9745
|
+
rgb(var(--background-end-rgb))
|
|
9746
|
+
)
|
|
9747
|
+
rgb(var(--background-start-rgb));
|
|
9748
|
+
}
|
|
9749
|
+
|
|
9750
|
+
@layer utilities {
|
|
9751
|
+
.text-balance {
|
|
9752
|
+
text-wrap: balance;
|
|
9753
|
+
}
|
|
9754
|
+
}
|
|
9755
|
+
`,
|
|
9756
|
+
// shadcn/ui base components
|
|
9757
|
+
"components/ui/button.tsx": `import * as React from "react"
|
|
9758
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
9759
|
+
import { cn } from "@/lib/utils"
|
|
9760
|
+
|
|
9761
|
+
const buttonVariants = cva(
|
|
9762
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
9763
|
+
{
|
|
9764
|
+
variants: {
|
|
9765
|
+
variant: {
|
|
9766
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
9767
|
+
destructive:
|
|
9768
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
9769
|
+
outline:
|
|
9770
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
9771
|
+
secondary:
|
|
9772
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
9773
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
9774
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
9775
|
+
},
|
|
9776
|
+
size: {
|
|
9777
|
+
default: "h-10 px-4 py-2",
|
|
9778
|
+
sm: "h-9 rounded-md px-3",
|
|
9779
|
+
lg: "h-11 rounded-md px-8",
|
|
9780
|
+
icon: "h-10 w-10",
|
|
9781
|
+
},
|
|
9782
|
+
},
|
|
9783
|
+
defaultVariants: {
|
|
9784
|
+
variant: "default",
|
|
9785
|
+
size: "default",
|
|
9786
|
+
},
|
|
9787
|
+
}
|
|
9788
|
+
)
|
|
9789
|
+
|
|
9790
|
+
export interface ButtonProps
|
|
9791
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
9792
|
+
VariantProps<typeof buttonVariants> {
|
|
9793
|
+
asChild?: boolean
|
|
9794
|
+
}
|
|
9795
|
+
|
|
9796
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
9797
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
9798
|
+
const Comp = asChild ? Slot : "button"
|
|
9799
|
+
return (
|
|
9800
|
+
<Comp
|
|
9801
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
9802
|
+
ref={ref}
|
|
9803
|
+
{...props}
|
|
9804
|
+
/>
|
|
9805
|
+
)
|
|
9806
|
+
}
|
|
9807
|
+
)
|
|
9808
|
+
Button.displayName = "Button"
|
|
9809
|
+
|
|
9810
|
+
export { Button, buttonVariants }
|
|
9811
|
+
`,
|
|
9812
|
+
"components/ui/card.tsx": `import * as React from "react"
|
|
9813
|
+
import { cn } from "@/lib/utils"
|
|
9814
|
+
|
|
9815
|
+
const Card = React.forwardRef<
|
|
9816
|
+
HTMLDivElement,
|
|
9817
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9818
|
+
>(({ className, ...props }, ref) => (
|
|
9819
|
+
<div
|
|
9820
|
+
ref={ref}
|
|
9821
|
+
className={cn(
|
|
9822
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
9823
|
+
className
|
|
9824
|
+
)}
|
|
9825
|
+
{...props}
|
|
9826
|
+
/>
|
|
9827
|
+
))
|
|
9828
|
+
Card.displayName = "Card"
|
|
9829
|
+
|
|
9830
|
+
const CardHeader = React.forwardRef<
|
|
9831
|
+
HTMLDivElement,
|
|
9832
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9833
|
+
>(({ className, ...props }, ref) => (
|
|
9834
|
+
<div
|
|
9835
|
+
ref={ref}
|
|
9836
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
9837
|
+
{...props}
|
|
9838
|
+
/>
|
|
9839
|
+
))
|
|
9840
|
+
CardHeader.displayName = "CardHeader"
|
|
9841
|
+
|
|
9842
|
+
const CardTitle = React.forwardRef<
|
|
9843
|
+
HTMLParagraphElement,
|
|
9844
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
9845
|
+
>(({ className, ...props }, ref) => (
|
|
9846
|
+
<h3
|
|
9847
|
+
ref={ref}
|
|
9848
|
+
className={cn(
|
|
9849
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
9850
|
+
className
|
|
9851
|
+
)}
|
|
9852
|
+
{...props}
|
|
9853
|
+
/>
|
|
9854
|
+
))
|
|
9855
|
+
CardTitle.displayName = "CardTitle"
|
|
9856
|
+
|
|
9857
|
+
const CardDescription = React.forwardRef<
|
|
9858
|
+
HTMLParagraphElement,
|
|
9859
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
9860
|
+
>(({ className, ...props }, ref) => (
|
|
9861
|
+
<p
|
|
9862
|
+
ref={ref}
|
|
9863
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
9864
|
+
{...props}
|
|
9865
|
+
/>
|
|
9866
|
+
))
|
|
9867
|
+
CardDescription.displayName = "CardDescription"
|
|
9868
|
+
|
|
9869
|
+
const CardContent = React.forwardRef<
|
|
9870
|
+
HTMLDivElement,
|
|
9871
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9872
|
+
>(({ className, ...props }, ref) => (
|
|
9873
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
9874
|
+
))
|
|
9875
|
+
CardContent.displayName = "CardContent"
|
|
9876
|
+
|
|
9877
|
+
const CardFooter = React.forwardRef<
|
|
9878
|
+
HTMLDivElement,
|
|
9879
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9880
|
+
>(({ className, ...props }, ref) => (
|
|
9881
|
+
<div
|
|
9882
|
+
ref={ref}
|
|
9883
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
9884
|
+
{...props}
|
|
9885
|
+
/>
|
|
9886
|
+
))
|
|
9887
|
+
CardFooter.displayName = "CardFooter"
|
|
9888
|
+
|
|
9889
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
9890
|
+
`,
|
|
9891
|
+
"components/ui/input.tsx": `import * as React from "react"
|
|
9892
|
+
import { cn } from "@/lib/utils"
|
|
9893
|
+
|
|
9894
|
+
export interface InputProps
|
|
9895
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
9896
|
+
|
|
9897
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
9898
|
+
({ className, type, ...props }, ref) => {
|
|
9899
|
+
return (
|
|
9900
|
+
<input
|
|
9901
|
+
type={type}
|
|
9902
|
+
className={cn(
|
|
9903
|
+
"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
9904
|
+
className
|
|
9905
|
+
)}
|
|
9906
|
+
ref={ref}
|
|
9907
|
+
{...props}
|
|
9908
|
+
/>
|
|
9909
|
+
)
|
|
9910
|
+
}
|
|
9911
|
+
)
|
|
9912
|
+
Input.displayName = "Input"
|
|
9913
|
+
|
|
9914
|
+
export { Input }
|
|
9915
|
+
`,
|
|
9916
|
+
"lib/utils.ts": `import { type ClassValue, clsx } from "clsx"
|
|
9917
|
+
import { twMerge } from "tailwind-merge"
|
|
9918
|
+
|
|
9919
|
+
export function cn(...inputs: ClassValue[]) {
|
|
9920
|
+
return twMerge(clsx(inputs))
|
|
9921
|
+
}
|
|
9922
|
+
`,
|
|
9923
|
+
// Add clsx and tailwind-merge to package.json dependencies
|
|
9924
|
+
"package.json.full": `{
|
|
9925
|
+
"name": "{{NAME}}",
|
|
9926
|
+
"version": "0.1.0",
|
|
9927
|
+
"private": true,
|
|
9928
|
+
"scripts": {
|
|
9929
|
+
"dev": "next dev",
|
|
9930
|
+
"build": "next build",
|
|
9931
|
+
"start": "next start",
|
|
9932
|
+
"lint": "next lint"
|
|
9933
|
+
},
|
|
9934
|
+
"dependencies": {
|
|
9935
|
+
"next": "^14.1.0",
|
|
9936
|
+
"react": "^18.2.0",
|
|
9937
|
+
"react-dom": "^18.2.0",
|
|
9938
|
+
"class-variance-authority": "^0.7.0",
|
|
9939
|
+
"clsx": "^2.1.0",
|
|
9940
|
+
"tailwind-merge": "^2.2.0"
|
|
9941
|
+
},
|
|
9942
|
+
"devDependencies": {
|
|
9943
|
+
"@types/node": "^20.11.0",
|
|
9944
|
+
"@types/react": "^18.2.0",
|
|
9945
|
+
"@types/react-dom": "^18.2.0",
|
|
9946
|
+
"typescript": "^5.3.0",
|
|
9947
|
+
"tailwindcss": "^3.4.0",
|
|
9948
|
+
"postcss": "^8.4.0",
|
|
9949
|
+
"autoprefixer": "^10.4.0",
|
|
9950
|
+
"eslint": "^8.56.0",
|
|
9951
|
+
"eslint-config-next": "^14.1.0"
|
|
9952
|
+
}
|
|
9953
|
+
}
|
|
9954
|
+
`
|
|
9955
|
+
};
|
|
9956
|
+
async function createFile(filePath, content, projectName) {
|
|
9957
|
+
const dir = path25.dirname(filePath);
|
|
9958
|
+
await fs23.mkdir(dir, { recursive: true });
|
|
9959
|
+
const replacedContent = content.replace(/{{NAME}}/g, projectName);
|
|
9960
|
+
await fs23.writeFile(filePath, replacedContent, "utf-8");
|
|
9961
|
+
}
|
|
9962
|
+
async function createNextApp(args) {
|
|
9963
|
+
const {
|
|
9964
|
+
name,
|
|
9965
|
+
directory,
|
|
9966
|
+
template = "full"
|
|
9967
|
+
} = args;
|
|
9968
|
+
try {
|
|
9969
|
+
if (!name || typeof name !== "string") {
|
|
9970
|
+
return { success: false, error: "name is required and must be a string" };
|
|
9971
|
+
}
|
|
9972
|
+
if (!/^[a-z0-9-_]+$/.test(name)) {
|
|
9973
|
+
return {
|
|
9974
|
+
success: false,
|
|
9975
|
+
error: "name must contain only lowercase letters, numbers, hyphens, and underscores"
|
|
9976
|
+
};
|
|
9977
|
+
}
|
|
9978
|
+
const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
|
|
9979
|
+
const projectPath = path25.join(baseDir, name);
|
|
9980
|
+
try {
|
|
9981
|
+
await fs23.access(projectPath);
|
|
9982
|
+
return {
|
|
9983
|
+
success: false,
|
|
9984
|
+
error: `Directory already exists: ${projectPath}. Remove it first or choose a different name.`
|
|
9985
|
+
};
|
|
9986
|
+
} catch {
|
|
9987
|
+
}
|
|
9988
|
+
await fs23.mkdir(projectPath, { recursive: true });
|
|
9989
|
+
const filesToCreate = template === "minimal" ? MINIMAL_FILES : FULL_FILES;
|
|
9990
|
+
const filesCreated = [];
|
|
9991
|
+
for (const [relativePath, content] of Object.entries(filesToCreate)) {
|
|
9992
|
+
if (relativePath === "package.json.full") continue;
|
|
9993
|
+
const fullPath = path25.join(projectPath, relativePath);
|
|
9994
|
+
await createFile(fullPath, content, name);
|
|
9995
|
+
filesCreated.push(relativePath);
|
|
9996
|
+
}
|
|
9997
|
+
if (template === "full") {
|
|
9998
|
+
const packageJsonPath = path25.join(projectPath, "package.json");
|
|
9999
|
+
const fullPackageJson = FULL_FILES["package.json.full"];
|
|
10000
|
+
if (fullPackageJson) {
|
|
10001
|
+
await createFile(packageJsonPath, fullPackageJson, name);
|
|
10002
|
+
}
|
|
10003
|
+
}
|
|
10004
|
+
console.log(`[create-next-app] Installing dependencies in ${projectPath}...`);
|
|
10005
|
+
const installResult = await shellCommand({
|
|
10006
|
+
command: "npm install",
|
|
10007
|
+
cwd: projectPath,
|
|
10008
|
+
timeout: 300
|
|
10009
|
+
// 5 minutos
|
|
10010
|
+
});
|
|
10011
|
+
const installJson = JSON.parse(installResult);
|
|
10012
|
+
if (installJson.status !== "success") {
|
|
10013
|
+
console.warn("[create-next-app] npm install warnings:", installJson.stderr);
|
|
10014
|
+
}
|
|
10015
|
+
const nextSteps = [
|
|
10016
|
+
`cd ${name}`,
|
|
10017
|
+
"npm run dev",
|
|
10018
|
+
"Open http://localhost:3000",
|
|
10019
|
+
template === "full" ? "Components shadcn/ui dispon\xEDveis em components/ui/" : "Adicione componentes conforme necess\xE1rio"
|
|
10020
|
+
];
|
|
10021
|
+
console.log(`[create-next-app] Projeto criado: ${projectPath}`);
|
|
10022
|
+
return {
|
|
10023
|
+
success: true,
|
|
10024
|
+
projectPath,
|
|
10025
|
+
directory: name,
|
|
10026
|
+
filesCreated,
|
|
10027
|
+
nextSteps
|
|
10028
|
+
};
|
|
10029
|
+
} catch (error) {
|
|
10030
|
+
console.error("[create-next-app] Error:", error.message);
|
|
10031
|
+
return {
|
|
10032
|
+
success: false,
|
|
10033
|
+
error: error.message || String(error)
|
|
10034
|
+
};
|
|
10035
|
+
}
|
|
10036
|
+
}
|
|
10037
|
+
|
|
10038
|
+
// src/app/agent/tools/natives/deploy-app.ts
|
|
10039
|
+
init_sandbox_policy();
|
|
10040
|
+
import { promises as fs24 } from "fs";
|
|
10041
|
+
import path26 from "path";
|
|
10042
|
+
var EXCLUDE_PATTERNS = [
|
|
10043
|
+
"node_modules",
|
|
10044
|
+
".next",
|
|
10045
|
+
".git",
|
|
10046
|
+
".gitignore",
|
|
10047
|
+
"dist",
|
|
10048
|
+
"build",
|
|
10049
|
+
"*.log",
|
|
10050
|
+
".DS_Store",
|
|
10051
|
+
".env.local",
|
|
10052
|
+
".env*.local",
|
|
10053
|
+
"coverage",
|
|
10054
|
+
".vercel",
|
|
10055
|
+
".turbo"
|
|
10056
|
+
];
|
|
10057
|
+
async function createProjectZip(projectDir, zipPath) {
|
|
10058
|
+
const excludes = [];
|
|
10059
|
+
for (const pattern of EXCLUDE_PATTERNS) {
|
|
10060
|
+
excludes.push("--exclude", pattern);
|
|
10061
|
+
}
|
|
10062
|
+
const zipCommand = `zip -r "${zipPath}" . ${excludes.join(" ")}`;
|
|
10063
|
+
const result = await shellCommand({
|
|
10064
|
+
command: zipCommand,
|
|
10065
|
+
cwd: projectDir,
|
|
10066
|
+
timeout: 120
|
|
10067
|
+
// 2 minutos para zippar
|
|
10068
|
+
});
|
|
10069
|
+
const resultJson = JSON.parse(result);
|
|
10070
|
+
if (resultJson.status !== "success") {
|
|
10071
|
+
throw new Error(`Failed to create ZIP: ${resultJson.stderr}`);
|
|
10072
|
+
}
|
|
10073
|
+
return zipPath;
|
|
10074
|
+
}
|
|
10075
|
+
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey) {
|
|
10076
|
+
const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
|
|
10077
|
+
const curlArgs = [
|
|
10078
|
+
"-X",
|
|
10079
|
+
"POST",
|
|
10080
|
+
deployUrl,
|
|
10081
|
+
"-F",
|
|
10082
|
+
`file=@${zipPath}`
|
|
10083
|
+
];
|
|
10084
|
+
if (name) {
|
|
10085
|
+
curlArgs.push("-F", `name=${name}`);
|
|
10086
|
+
}
|
|
10087
|
+
if (apiKey) {
|
|
10088
|
+
curlArgs.push("-H", `Authorization: Bearer ${apiKey}`);
|
|
10089
|
+
curlArgs.push("-H", `X-API-Key: ${apiKey}`);
|
|
10090
|
+
}
|
|
10091
|
+
curlArgs.push("-H", "Accept: application/json");
|
|
10092
|
+
const curlCommand = `curl ${curlArgs.join(" ")}`;
|
|
10093
|
+
const result = await shellCommand({
|
|
10094
|
+
command: curlCommand,
|
|
10095
|
+
timeout: 60
|
|
10096
|
+
// 1 minuto para upload
|
|
10097
|
+
});
|
|
10098
|
+
const resultJson = JSON.parse(result);
|
|
10099
|
+
if (resultJson.status !== "success") {
|
|
10100
|
+
throw new Error(`Upload failed: ${resultJson.stderr}`);
|
|
10101
|
+
}
|
|
10102
|
+
try {
|
|
10103
|
+
const response = JSON.parse(resultJson.stdout);
|
|
10104
|
+
if (response.success === false || response.error) {
|
|
10105
|
+
const errorMsg = typeof response.error === "string" ? response.error : response.error?.message || "Deploy failed";
|
|
10106
|
+
return {
|
|
10107
|
+
success: false,
|
|
10108
|
+
error: errorMsg
|
|
10109
|
+
};
|
|
10110
|
+
}
|
|
10111
|
+
const data = response.data || {};
|
|
10112
|
+
return {
|
|
10113
|
+
success: true,
|
|
10114
|
+
appId: data.appId,
|
|
10115
|
+
name: data.name,
|
|
10116
|
+
status: data.status || "building",
|
|
10117
|
+
url: severinoUrl.replace(/\/$/, "") + `/app/${data.appId}`,
|
|
10118
|
+
message: data.message || "Deploy iniciado"
|
|
10119
|
+
};
|
|
10120
|
+
} catch (parseError) {
|
|
10121
|
+
throw new Error(`Failed to parse response: ${parseError.message}`);
|
|
10122
|
+
}
|
|
10123
|
+
}
|
|
10124
|
+
async function deployApp(args) {
|
|
10125
|
+
const envSeverinoUrl = process.env.SEVERINO_URL || "http://localhost:3000";
|
|
10126
|
+
const envApiKey = process.env.SEVERINO_API_KEY || void 0;
|
|
10127
|
+
const {
|
|
10128
|
+
projectDir,
|
|
10129
|
+
name,
|
|
10130
|
+
severinoUrl = envSeverinoUrl,
|
|
10131
|
+
apiKey = envApiKey
|
|
10132
|
+
} = args;
|
|
10133
|
+
try {
|
|
10134
|
+
if (!projectDir || typeof projectDir !== "string") {
|
|
10135
|
+
return { success: false, error: "projectDir is required" };
|
|
10136
|
+
}
|
|
10137
|
+
const resolvedProjectDir = resolveWorkspacePath(projectDir);
|
|
10138
|
+
try {
|
|
10139
|
+
await fs24.access(resolvedProjectDir);
|
|
10140
|
+
} catch {
|
|
10141
|
+
return {
|
|
10142
|
+
success: false,
|
|
10143
|
+
error: `Project directory not found: ${resolvedProjectDir}`
|
|
10144
|
+
};
|
|
10145
|
+
}
|
|
10146
|
+
const packageJsonPath = path26.join(resolvedProjectDir, "package.json");
|
|
10147
|
+
try {
|
|
10148
|
+
await fs24.access(packageJsonPath);
|
|
10149
|
+
} catch {
|
|
10150
|
+
return {
|
|
10151
|
+
success: false,
|
|
10152
|
+
error: "Not a Next.js project: package.json not found"
|
|
10153
|
+
};
|
|
10154
|
+
}
|
|
10155
|
+
const packageJsonContent = await fs24.readFile(packageJsonPath, "utf-8");
|
|
10156
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
10157
|
+
const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
10158
|
+
if (!hasNext) {
|
|
10159
|
+
return {
|
|
10160
|
+
success: false,
|
|
10161
|
+
error: "Not a Next.js project: next not found in dependencies"
|
|
10162
|
+
};
|
|
10163
|
+
}
|
|
10164
|
+
const appName = name || packageJson.name || path26.basename(resolvedProjectDir);
|
|
10165
|
+
const tempDir = path26.join(resolvedProjectDir, ".tmp");
|
|
10166
|
+
await fs24.mkdir(tempDir, { recursive: true });
|
|
10167
|
+
const zipPath = path26.join(tempDir, `${appName}-${Date.now()}.zip`);
|
|
10168
|
+
console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
|
|
10169
|
+
await createProjectZip(resolvedProjectDir, zipPath);
|
|
10170
|
+
const zipStats = await fs24.stat(zipPath);
|
|
10171
|
+
const zipSizeMB = zipStats.size / 1024 / 1024;
|
|
10172
|
+
if (zipSizeMB > 50) {
|
|
10173
|
+
await fs24.unlink(zipPath).catch(() => {
|
|
10174
|
+
});
|
|
10175
|
+
await fs24.rmdir(tempDir).catch(() => {
|
|
10176
|
+
});
|
|
10177
|
+
return {
|
|
10178
|
+
success: false,
|
|
10179
|
+
error: `ZIP too large: ${zipSizeMB.toFixed(2)}MB (max: 50MB)`
|
|
10180
|
+
};
|
|
10181
|
+
}
|
|
10182
|
+
console.log(`[deploy-app] ZIP size: ${zipSizeMB.toFixed(2)}MB`);
|
|
10183
|
+
console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
|
|
10184
|
+
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey);
|
|
10185
|
+
try {
|
|
10186
|
+
await fs24.unlink(zipPath);
|
|
10187
|
+
await fs24.rmdir(tempDir);
|
|
10188
|
+
} catch (e) {
|
|
10189
|
+
console.warn("[deploy-app] Cleanup warning:", e);
|
|
10190
|
+
}
|
|
10191
|
+
if (deployResult.success) {
|
|
10192
|
+
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
10193
|
+
}
|
|
10194
|
+
return deployResult;
|
|
10195
|
+
} catch (error) {
|
|
10196
|
+
console.error("[deploy-app] Error:", error.message);
|
|
10197
|
+
return {
|
|
10198
|
+
success: false,
|
|
10199
|
+
error: error.message || String(error)
|
|
10200
|
+
};
|
|
10201
|
+
}
|
|
10202
|
+
}
|
|
10203
|
+
|
|
10204
|
+
// src/app/agent/runtime/native_tool_catalog.ts
|
|
10205
|
+
init_sandbox_policy();
|
|
9213
10206
|
var NATIVE_TOOL_ENTRIES = [
|
|
9214
10207
|
{
|
|
9215
10208
|
metadata: {
|
|
@@ -9773,6 +10766,28 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
9773
10766
|
description: "Kill a running worker by session_id. Use when a worker is stuck or needs to be cancelled immediately. Sends SIGTERM and cleans up mailbox."
|
|
9774
10767
|
},
|
|
9775
10768
|
implementation: killAgent
|
|
10769
|
+
},
|
|
10770
|
+
{
|
|
10771
|
+
metadata: {
|
|
10772
|
+
name: "create_next_app",
|
|
10773
|
+
category: "filesystem",
|
|
10774
|
+
riskLevel: "write",
|
|
10775
|
+
autoApproveInLocal: false,
|
|
10776
|
+
autoApproveInSandbox: true,
|
|
10777
|
+
description: "Create a new Next.js project instantly with App Router, shadcn/ui components, Tailwind CSS, and TypeScript. Templates: minimal (basic structure) or full (with pre-built UI components)."
|
|
10778
|
+
},
|
|
10779
|
+
implementation: createNextApp
|
|
10780
|
+
},
|
|
10781
|
+
{
|
|
10782
|
+
metadata: {
|
|
10783
|
+
name: "deploy_app",
|
|
10784
|
+
category: "execution",
|
|
10785
|
+
riskLevel: "network",
|
|
10786
|
+
autoApproveInLocal: false,
|
|
10787
|
+
autoApproveInSandbox: true,
|
|
10788
|
+
description: "Deploy a Next.js project to Severino. Zips the project (excluding node_modules, .next) and uploads to /api/v1/deploy. Returns appId and live URL."
|
|
10789
|
+
},
|
|
10790
|
+
implementation: deployApp
|
|
9776
10791
|
}
|
|
9777
10792
|
];
|
|
9778
10793
|
var TOOL_METADATA_MAP = new Map(
|
|
@@ -9788,6 +10803,10 @@ function getNativeToolImplementation(toolName) {
|
|
|
9788
10803
|
return TOOL_IMPLEMENTATION_MAP.get(toolName);
|
|
9789
10804
|
}
|
|
9790
10805
|
function getAllNativeToolMetadata() {
|
|
10806
|
+
const policy = getSandboxPolicy();
|
|
10807
|
+
if (!policy.isSandbox) {
|
|
10808
|
+
return NATIVE_TOOL_ENTRIES.filter((entry) => entry.metadata.autoApproveInLocal !== false || !["create_next_app", "deploy_app"].includes(entry.metadata.name)).map((entry) => entry.metadata);
|
|
10809
|
+
}
|
|
9791
10810
|
return NATIVE_TOOL_ENTRIES.map((entry) => entry.metadata);
|
|
9792
10811
|
}
|
|
9793
10812
|
function applyMetadataToToolDefinitions(toolDefinitions) {
|
|
@@ -9813,9 +10832,9 @@ var ToolInvoker = class {
|
|
|
9813
10832
|
async initialize() {
|
|
9814
10833
|
try {
|
|
9815
10834
|
const __filename = fileURLToPath2(import.meta.url);
|
|
9816
|
-
const __dirname =
|
|
9817
|
-
const configPath =
|
|
9818
|
-
const fileContent = await
|
|
10835
|
+
const __dirname = path27.dirname(__filename);
|
|
10836
|
+
const configPath = path27.resolve(__dirname, "config", "native_tools.json");
|
|
10837
|
+
const fileContent = await fs25.readFile(configPath, "utf-8");
|
|
9819
10838
|
const config2 = JSON.parse(fileContent);
|
|
9820
10839
|
this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
|
|
9821
10840
|
} catch (error) {
|
|
@@ -9857,9 +10876,9 @@ var ToolInvoker = class {
|
|
|
9857
10876
|
};
|
|
9858
10877
|
|
|
9859
10878
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
9860
|
-
import { promises as
|
|
9861
|
-
import
|
|
9862
|
-
import
|
|
10879
|
+
import { promises as fs26 } from "fs";
|
|
10880
|
+
import path28 from "path";
|
|
10881
|
+
import os16 from "os";
|
|
9863
10882
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9864
10883
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9865
10884
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -9886,9 +10905,9 @@ var MCPClient = class {
|
|
|
9886
10905
|
});
|
|
9887
10906
|
}
|
|
9888
10907
|
const __filename = fileURLToPath3(import.meta.url);
|
|
9889
|
-
const __dirname =
|
|
9890
|
-
const defaultConfigPath =
|
|
9891
|
-
const userConfigPath =
|
|
10908
|
+
const __dirname = path28.dirname(__filename);
|
|
10909
|
+
const defaultConfigPath = path28.resolve(__dirname, "config", "bluma-mcp.json");
|
|
10910
|
+
const userConfigPath = path28.join(os16.homedir(), ".bluma", "bluma-mcp.json");
|
|
9892
10911
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
9893
10912
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
9894
10913
|
const mergedConfig = {
|
|
@@ -9922,7 +10941,7 @@ var MCPClient = class {
|
|
|
9922
10941
|
}
|
|
9923
10942
|
async loadMcpConfig(configPath, configType) {
|
|
9924
10943
|
try {
|
|
9925
|
-
const fileContent = await
|
|
10944
|
+
const fileContent = await fs26.readFile(configPath, "utf-8");
|
|
9926
10945
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
9927
10946
|
return JSON.parse(processedContent);
|
|
9928
10947
|
} catch (error) {
|
|
@@ -9941,7 +10960,7 @@ var MCPClient = class {
|
|
|
9941
10960
|
async connectToStdioServer(serverName, config2) {
|
|
9942
10961
|
let commandToExecute = config2.command;
|
|
9943
10962
|
let argsToExecute = config2.args || [];
|
|
9944
|
-
const isWindows =
|
|
10963
|
+
const isWindows = os16.platform() === "win32";
|
|
9945
10964
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
9946
10965
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
9947
10966
|
commandToExecute = argsToExecute[1];
|
|
@@ -10099,13 +11118,13 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
10099
11118
|
};
|
|
10100
11119
|
|
|
10101
11120
|
// src/app/agent/bluma/core/bluma.ts
|
|
10102
|
-
import
|
|
11121
|
+
import path37 from "path";
|
|
10103
11122
|
import { v4 as uuidv48 } from "uuid";
|
|
10104
11123
|
|
|
10105
11124
|
// src/app/agent/session_manager/session_manager.ts
|
|
10106
|
-
import
|
|
10107
|
-
import
|
|
10108
|
-
import { promises as
|
|
11125
|
+
import path29 from "path";
|
|
11126
|
+
import os17 from "os";
|
|
11127
|
+
import { promises as fs27 } from "fs";
|
|
10109
11128
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
10110
11129
|
async function withFileLock(file, fn) {
|
|
10111
11130
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -10141,13 +11160,13 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
10141
11160
|
function expandHome(p) {
|
|
10142
11161
|
if (!p) return p;
|
|
10143
11162
|
if (p.startsWith("~")) {
|
|
10144
|
-
return
|
|
11163
|
+
return path29.join(os17.homedir(), p.slice(1));
|
|
10145
11164
|
}
|
|
10146
11165
|
return p;
|
|
10147
11166
|
}
|
|
10148
11167
|
function getPreferredAppDir() {
|
|
10149
|
-
const fixed =
|
|
10150
|
-
return
|
|
11168
|
+
const fixed = path29.join(os17.homedir(), ".bluma");
|
|
11169
|
+
return path29.resolve(expandHome(fixed));
|
|
10151
11170
|
}
|
|
10152
11171
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
10153
11172
|
let attempt = 0;
|
|
@@ -10155,10 +11174,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10155
11174
|
const isWin = process.platform === "win32";
|
|
10156
11175
|
while (attempt <= maxRetries) {
|
|
10157
11176
|
try {
|
|
10158
|
-
const dir =
|
|
10159
|
-
await
|
|
11177
|
+
const dir = path29.dirname(dest);
|
|
11178
|
+
await fs27.mkdir(dir, { recursive: true }).catch(() => {
|
|
10160
11179
|
});
|
|
10161
|
-
await
|
|
11180
|
+
await fs27.rename(src, dest);
|
|
10162
11181
|
return;
|
|
10163
11182
|
} catch (e) {
|
|
10164
11183
|
lastErr = e;
|
|
@@ -10171,13 +11190,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10171
11190
|
}
|
|
10172
11191
|
}
|
|
10173
11192
|
try {
|
|
10174
|
-
await
|
|
10175
|
-
const data = await
|
|
10176
|
-
const dir =
|
|
10177
|
-
await
|
|
11193
|
+
await fs27.access(src);
|
|
11194
|
+
const data = await fs27.readFile(src);
|
|
11195
|
+
const dir = path29.dirname(dest);
|
|
11196
|
+
await fs27.mkdir(dir, { recursive: true }).catch(() => {
|
|
10178
11197
|
});
|
|
10179
|
-
await
|
|
10180
|
-
await
|
|
11198
|
+
await fs27.writeFile(dest, data);
|
|
11199
|
+
await fs27.unlink(src).catch(() => {
|
|
10181
11200
|
});
|
|
10182
11201
|
return;
|
|
10183
11202
|
} catch (fallbackErr) {
|
|
@@ -10190,16 +11209,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10190
11209
|
}
|
|
10191
11210
|
async function ensureSessionDir() {
|
|
10192
11211
|
const appDir = getPreferredAppDir();
|
|
10193
|
-
const sessionDir =
|
|
10194
|
-
await
|
|
11212
|
+
const sessionDir = path29.join(appDir, "sessions");
|
|
11213
|
+
await fs27.mkdir(sessionDir, { recursive: true });
|
|
10195
11214
|
return sessionDir;
|
|
10196
11215
|
}
|
|
10197
11216
|
async function loadOrcreateSession(sessionId) {
|
|
10198
11217
|
const sessionDir = await ensureSessionDir();
|
|
10199
|
-
const sessionFile =
|
|
11218
|
+
const sessionFile = path29.join(sessionDir, `${sessionId}.json`);
|
|
10200
11219
|
try {
|
|
10201
|
-
await
|
|
10202
|
-
const fileContent = await
|
|
11220
|
+
await fs27.access(sessionFile);
|
|
11221
|
+
const fileContent = await fs27.readFile(sessionFile, "utf-8");
|
|
10203
11222
|
const sessionData = JSON.parse(fileContent);
|
|
10204
11223
|
const memory = {
|
|
10205
11224
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -10212,7 +11231,7 @@ async function loadOrcreateSession(sessionId) {
|
|
|
10212
11231
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10213
11232
|
conversation_history: []
|
|
10214
11233
|
};
|
|
10215
|
-
await
|
|
11234
|
+
await fs27.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
10216
11235
|
const emptyMemory = {
|
|
10217
11236
|
historyAnchor: null,
|
|
10218
11237
|
compressedTurnSliceCount: 0
|
|
@@ -10224,12 +11243,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10224
11243
|
await withFileLock(sessionFile, async () => {
|
|
10225
11244
|
let sessionData;
|
|
10226
11245
|
try {
|
|
10227
|
-
const dir =
|
|
10228
|
-
await
|
|
11246
|
+
const dir = path29.dirname(sessionFile);
|
|
11247
|
+
await fs27.mkdir(dir, { recursive: true });
|
|
10229
11248
|
} catch {
|
|
10230
11249
|
}
|
|
10231
11250
|
try {
|
|
10232
|
-
const fileContent = await
|
|
11251
|
+
const fileContent = await fs27.readFile(sessionFile, "utf-8");
|
|
10233
11252
|
sessionData = JSON.parse(fileContent);
|
|
10234
11253
|
} catch (error) {
|
|
10235
11254
|
const code = error && error.code;
|
|
@@ -10240,14 +11259,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10240
11259
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
10241
11260
|
}
|
|
10242
11261
|
}
|
|
10243
|
-
const sessionId =
|
|
11262
|
+
const sessionId = path29.basename(sessionFile, ".json");
|
|
10244
11263
|
sessionData = {
|
|
10245
11264
|
session_id: sessionId,
|
|
10246
11265
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10247
11266
|
conversation_history: []
|
|
10248
11267
|
};
|
|
10249
11268
|
try {
|
|
10250
|
-
await
|
|
11269
|
+
await fs27.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
10251
11270
|
} catch {
|
|
10252
11271
|
}
|
|
10253
11272
|
}
|
|
@@ -10263,7 +11282,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10263
11282
|
}
|
|
10264
11283
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
10265
11284
|
try {
|
|
10266
|
-
await
|
|
11285
|
+
await fs27.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
10267
11286
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
10268
11287
|
} catch (writeError) {
|
|
10269
11288
|
if (writeError instanceof Error) {
|
|
@@ -10272,7 +11291,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10272
11291
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
10273
11292
|
}
|
|
10274
11293
|
try {
|
|
10275
|
-
await
|
|
11294
|
+
await fs27.unlink(tempSessionFile);
|
|
10276
11295
|
} catch {
|
|
10277
11296
|
}
|
|
10278
11297
|
}
|
|
@@ -10289,15 +11308,15 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
10289
11308
|
}
|
|
10290
11309
|
|
|
10291
11310
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
10292
|
-
import
|
|
10293
|
-
import
|
|
10294
|
-
import
|
|
11311
|
+
import os21 from "os";
|
|
11312
|
+
import fs32 from "fs";
|
|
11313
|
+
import path34 from "path";
|
|
10295
11314
|
import { execSync as execSync3 } from "child_process";
|
|
10296
11315
|
|
|
10297
11316
|
// src/app/agent/skills/skill_loader.ts
|
|
10298
|
-
import
|
|
10299
|
-
import
|
|
10300
|
-
import
|
|
11317
|
+
import fs28 from "fs";
|
|
11318
|
+
import path30 from "path";
|
|
11319
|
+
import os18 from "os";
|
|
10301
11320
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10302
11321
|
var SkillLoader = class _SkillLoader {
|
|
10303
11322
|
bundledSkillsDir;
|
|
@@ -10306,8 +11325,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10306
11325
|
cache = /* @__PURE__ */ new Map();
|
|
10307
11326
|
conflicts = [];
|
|
10308
11327
|
constructor(projectRoot, bundledDir) {
|
|
10309
|
-
this.projectSkillsDir =
|
|
10310
|
-
this.globalSkillsDir =
|
|
11328
|
+
this.projectSkillsDir = path30.join(projectRoot, ".bluma", "skills");
|
|
11329
|
+
this.globalSkillsDir = path30.join(os18.homedir(), ".bluma", "skills");
|
|
10311
11330
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
10312
11331
|
}
|
|
10313
11332
|
/**
|
|
@@ -10316,48 +11335,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
10316
11335
|
*/
|
|
10317
11336
|
static resolveBundledDir() {
|
|
10318
11337
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
10319
|
-
return
|
|
11338
|
+
return path30.join(process.cwd(), "dist", "config", "skills");
|
|
10320
11339
|
}
|
|
10321
11340
|
const candidates = [];
|
|
10322
11341
|
const push = (p) => {
|
|
10323
|
-
const abs =
|
|
11342
|
+
const abs = path30.resolve(p);
|
|
10324
11343
|
if (!candidates.includes(abs)) {
|
|
10325
11344
|
candidates.push(abs);
|
|
10326
11345
|
}
|
|
10327
11346
|
};
|
|
10328
11347
|
let argvBundled = null;
|
|
10329
11348
|
try {
|
|
10330
|
-
const bundleDir =
|
|
10331
|
-
push(
|
|
11349
|
+
const bundleDir = path30.dirname(fileURLToPath4(import.meta.url));
|
|
11350
|
+
push(path30.join(bundleDir, "config", "skills"));
|
|
10332
11351
|
} catch {
|
|
10333
11352
|
}
|
|
10334
11353
|
const argv1 = process.argv[1];
|
|
10335
11354
|
if (argv1 && !argv1.startsWith("-")) {
|
|
10336
11355
|
try {
|
|
10337
11356
|
let resolved = argv1;
|
|
10338
|
-
if (
|
|
10339
|
-
resolved =
|
|
10340
|
-
} else if (!
|
|
10341
|
-
resolved =
|
|
11357
|
+
if (path30.isAbsolute(argv1) && fs28.existsSync(argv1)) {
|
|
11358
|
+
resolved = fs28.realpathSync(argv1);
|
|
11359
|
+
} else if (!path30.isAbsolute(argv1)) {
|
|
11360
|
+
resolved = path30.resolve(process.cwd(), argv1);
|
|
10342
11361
|
}
|
|
10343
|
-
const scriptDir =
|
|
10344
|
-
argvBundled =
|
|
11362
|
+
const scriptDir = path30.dirname(resolved);
|
|
11363
|
+
argvBundled = path30.join(scriptDir, "config", "skills");
|
|
10345
11364
|
push(argvBundled);
|
|
10346
11365
|
} catch {
|
|
10347
11366
|
}
|
|
10348
11367
|
}
|
|
10349
11368
|
for (const abs of candidates) {
|
|
10350
|
-
if (
|
|
11369
|
+
if (fs28.existsSync(abs)) {
|
|
10351
11370
|
return abs;
|
|
10352
11371
|
}
|
|
10353
11372
|
}
|
|
10354
11373
|
try {
|
|
10355
|
-
return
|
|
11374
|
+
return path30.join(path30.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
10356
11375
|
} catch {
|
|
10357
11376
|
if (argvBundled) {
|
|
10358
11377
|
return argvBundled;
|
|
10359
11378
|
}
|
|
10360
|
-
return
|
|
11379
|
+
return path30.join(os18.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
10361
11380
|
}
|
|
10362
11381
|
}
|
|
10363
11382
|
/**
|
|
@@ -10386,8 +11405,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10386
11405
|
this.conflicts.push({
|
|
10387
11406
|
name: skill.name,
|
|
10388
11407
|
userSource: source,
|
|
10389
|
-
userPath:
|
|
10390
|
-
bundledPath:
|
|
11408
|
+
userPath: path30.join(dir, skill.name, "SKILL.md"),
|
|
11409
|
+
bundledPath: path30.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
10391
11410
|
});
|
|
10392
11411
|
continue;
|
|
10393
11412
|
}
|
|
@@ -10395,20 +11414,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
10395
11414
|
}
|
|
10396
11415
|
}
|
|
10397
11416
|
listFromDir(dir, source) {
|
|
10398
|
-
if (!
|
|
11417
|
+
if (!fs28.existsSync(dir)) return [];
|
|
10399
11418
|
try {
|
|
10400
|
-
return
|
|
10401
|
-
const fullPath =
|
|
10402
|
-
return
|
|
10403
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
11419
|
+
return fs28.readdirSync(dir).filter((d) => {
|
|
11420
|
+
const fullPath = path30.join(dir, d);
|
|
11421
|
+
return fs28.statSync(fullPath).isDirectory() && fs28.existsSync(path30.join(fullPath, "SKILL.md"));
|
|
11422
|
+
}).map((d) => this.loadMetadataFromPath(path30.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
10404
11423
|
} catch {
|
|
10405
11424
|
return [];
|
|
10406
11425
|
}
|
|
10407
11426
|
}
|
|
10408
11427
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
10409
|
-
if (!
|
|
11428
|
+
if (!fs28.existsSync(skillPath)) return null;
|
|
10410
11429
|
try {
|
|
10411
|
-
const raw =
|
|
11430
|
+
const raw = fs28.readFileSync(skillPath, "utf-8");
|
|
10412
11431
|
const parsed = this.parseFrontmatter(raw);
|
|
10413
11432
|
return {
|
|
10414
11433
|
name: parsed.name || skillName,
|
|
@@ -10430,12 +11449,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
10430
11449
|
*/
|
|
10431
11450
|
load(name) {
|
|
10432
11451
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
10433
|
-
const bundledPath =
|
|
10434
|
-
const projectPath =
|
|
10435
|
-
const globalPath =
|
|
10436
|
-
const existsBundled =
|
|
10437
|
-
const existsProject =
|
|
10438
|
-
const existsGlobal =
|
|
11452
|
+
const bundledPath = path30.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
11453
|
+
const projectPath = path30.join(this.projectSkillsDir, name, "SKILL.md");
|
|
11454
|
+
const globalPath = path30.join(this.globalSkillsDir, name, "SKILL.md");
|
|
11455
|
+
const existsBundled = fs28.existsSync(bundledPath);
|
|
11456
|
+
const existsProject = fs28.existsSync(projectPath);
|
|
11457
|
+
const existsGlobal = fs28.existsSync(globalPath);
|
|
10439
11458
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
10440
11459
|
const conflictSource = existsProject ? "project" : "global";
|
|
10441
11460
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -10474,9 +11493,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
10474
11493
|
}
|
|
10475
11494
|
loadFromPath(skillPath, name, source) {
|
|
10476
11495
|
try {
|
|
10477
|
-
const raw =
|
|
11496
|
+
const raw = fs28.readFileSync(skillPath, "utf-8");
|
|
10478
11497
|
const parsed = this.parseFrontmatter(raw);
|
|
10479
|
-
const skillDir =
|
|
11498
|
+
const skillDir = path30.dirname(skillPath);
|
|
10480
11499
|
return {
|
|
10481
11500
|
name: parsed.name || name,
|
|
10482
11501
|
description: parsed.description || "",
|
|
@@ -10485,22 +11504,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
10485
11504
|
version: parsed.version,
|
|
10486
11505
|
author: parsed.author,
|
|
10487
11506
|
license: parsed.license,
|
|
10488
|
-
references: this.scanAssets(
|
|
10489
|
-
scripts: this.scanAssets(
|
|
11507
|
+
references: this.scanAssets(path30.join(skillDir, "references")),
|
|
11508
|
+
scripts: this.scanAssets(path30.join(skillDir, "scripts"))
|
|
10490
11509
|
};
|
|
10491
11510
|
} catch {
|
|
10492
11511
|
return null;
|
|
10493
11512
|
}
|
|
10494
11513
|
}
|
|
10495
11514
|
scanAssets(dir) {
|
|
10496
|
-
if (!
|
|
11515
|
+
if (!fs28.existsSync(dir)) return [];
|
|
10497
11516
|
try {
|
|
10498
|
-
return
|
|
10499
|
-
const fp =
|
|
10500
|
-
return
|
|
11517
|
+
return fs28.readdirSync(dir).filter((f) => {
|
|
11518
|
+
const fp = path30.join(dir, f);
|
|
11519
|
+
return fs28.statSync(fp).isFile();
|
|
10501
11520
|
}).map((f) => ({
|
|
10502
11521
|
name: f,
|
|
10503
|
-
path:
|
|
11522
|
+
path: path30.resolve(dir, f)
|
|
10504
11523
|
}));
|
|
10505
11524
|
} catch {
|
|
10506
11525
|
return [];
|
|
@@ -10557,10 +11576,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
10557
11576
|
this.cache.clear();
|
|
10558
11577
|
}
|
|
10559
11578
|
exists(name) {
|
|
10560
|
-
const bundledPath =
|
|
10561
|
-
const projectPath =
|
|
10562
|
-
const globalPath =
|
|
10563
|
-
return
|
|
11579
|
+
const bundledPath = path30.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
11580
|
+
const projectPath = path30.join(this.projectSkillsDir, name, "SKILL.md");
|
|
11581
|
+
const globalPath = path30.join(this.globalSkillsDir, name, "SKILL.md");
|
|
11582
|
+
return fs28.existsSync(bundledPath) || fs28.existsSync(projectPath) || fs28.existsSync(globalPath);
|
|
10564
11583
|
}
|
|
10565
11584
|
/**
|
|
10566
11585
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -10592,8 +11611,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10592
11611
|
};
|
|
10593
11612
|
|
|
10594
11613
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
10595
|
-
import
|
|
10596
|
-
import
|
|
11614
|
+
import fs29 from "fs";
|
|
11615
|
+
import path31 from "path";
|
|
10597
11616
|
import { execSync as execSync2 } from "child_process";
|
|
10598
11617
|
var LIMITS = {
|
|
10599
11618
|
readme: 1e4,
|
|
@@ -10608,10 +11627,10 @@ var LIMITS = {
|
|
|
10608
11627
|
};
|
|
10609
11628
|
function safeReadFile(filePath, maxChars) {
|
|
10610
11629
|
try {
|
|
10611
|
-
if (!
|
|
10612
|
-
const st =
|
|
11630
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
11631
|
+
const st = fs29.statSync(filePath);
|
|
10613
11632
|
if (!st.isFile()) return null;
|
|
10614
|
-
const raw =
|
|
11633
|
+
const raw = fs29.readFileSync(filePath, "utf8");
|
|
10615
11634
|
if (raw.length <= maxChars) return raw;
|
|
10616
11635
|
return `${raw.slice(0, maxChars)}
|
|
10617
11636
|
|
|
@@ -10622,7 +11641,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
10622
11641
|
}
|
|
10623
11642
|
function tryReadReadme(cwd) {
|
|
10624
11643
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
10625
|
-
const c = safeReadFile(
|
|
11644
|
+
const c = safeReadFile(path31.join(cwd, name), LIMITS.readme);
|
|
10626
11645
|
if (c) return `(${name})
|
|
10627
11646
|
${c}`;
|
|
10628
11647
|
}
|
|
@@ -10630,14 +11649,14 @@ ${c}`;
|
|
|
10630
11649
|
}
|
|
10631
11650
|
function tryReadBluMaMd(cwd) {
|
|
10632
11651
|
const paths = [
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
11652
|
+
path31.join(cwd, "BluMa.md"),
|
|
11653
|
+
path31.join(cwd, "BLUMA.md"),
|
|
11654
|
+
path31.join(cwd, ".bluma", "BluMa.md")
|
|
10636
11655
|
];
|
|
10637
11656
|
for (const p of paths) {
|
|
10638
11657
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
10639
11658
|
if (c) {
|
|
10640
|
-
const rel =
|
|
11659
|
+
const rel = path31.relative(cwd, p) || p;
|
|
10641
11660
|
return `(${rel})
|
|
10642
11661
|
${c}`;
|
|
10643
11662
|
}
|
|
@@ -10645,10 +11664,10 @@ ${c}`;
|
|
|
10645
11664
|
return null;
|
|
10646
11665
|
}
|
|
10647
11666
|
function summarizePackageJson(cwd) {
|
|
10648
|
-
const p =
|
|
11667
|
+
const p = path31.join(cwd, "package.json");
|
|
10649
11668
|
try {
|
|
10650
|
-
if (!
|
|
10651
|
-
const pkg = JSON.parse(
|
|
11669
|
+
if (!fs29.existsSync(p)) return null;
|
|
11670
|
+
const pkg = JSON.parse(fs29.readFileSync(p, "utf8"));
|
|
10652
11671
|
const scripts = pkg.scripts;
|
|
10653
11672
|
let scriptKeys = "";
|
|
10654
11673
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -10681,7 +11700,7 @@ function summarizePackageJson(cwd) {
|
|
|
10681
11700
|
}
|
|
10682
11701
|
function topLevelListing(cwd) {
|
|
10683
11702
|
try {
|
|
10684
|
-
const names =
|
|
11703
|
+
const names = fs29.readdirSync(cwd, { withFileTypes: true });
|
|
10685
11704
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
10686
11705
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
10687
11706
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -10739,7 +11758,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
10739
11758
|
parts.push(pkg);
|
|
10740
11759
|
parts.push("```\n");
|
|
10741
11760
|
}
|
|
10742
|
-
const py = safeReadFile(
|
|
11761
|
+
const py = safeReadFile(path31.join(cwd, "pyproject.toml"), LIMITS.pyproject);
|
|
10743
11762
|
if (py) {
|
|
10744
11763
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
10745
11764
|
parts.push(py);
|
|
@@ -10757,15 +11776,15 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
10757
11776
|
parts.push(bluma);
|
|
10758
11777
|
parts.push("```\n");
|
|
10759
11778
|
}
|
|
10760
|
-
const contrib = safeReadFile(
|
|
11779
|
+
const contrib = safeReadFile(path31.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
10761
11780
|
if (contrib) {
|
|
10762
11781
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
10763
11782
|
parts.push(contrib);
|
|
10764
11783
|
parts.push("```\n");
|
|
10765
11784
|
}
|
|
10766
|
-
const chlog = safeReadFile(
|
|
11785
|
+
const chlog = safeReadFile(path31.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
|
|
10767
11786
|
if (!chlog) {
|
|
10768
|
-
const alt = safeReadFile(
|
|
11787
|
+
const alt = safeReadFile(path31.join(cwd, "CHANGES.md"), LIMITS.changelog);
|
|
10769
11788
|
if (alt) {
|
|
10770
11789
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
10771
11790
|
parts.push(alt);
|
|
@@ -10805,15 +11824,15 @@ init_runtime_config();
|
|
|
10805
11824
|
|
|
10806
11825
|
// src/app/agent/runtime/plugin_registry.ts
|
|
10807
11826
|
init_sandbox_policy();
|
|
10808
|
-
import
|
|
10809
|
-
import
|
|
10810
|
-
import
|
|
11827
|
+
import fs30 from "fs";
|
|
11828
|
+
import os19 from "os";
|
|
11829
|
+
import path32 from "path";
|
|
10811
11830
|
function getProjectPluginsDir() {
|
|
10812
11831
|
const policy = getSandboxPolicy();
|
|
10813
|
-
return
|
|
11832
|
+
return path32.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
10814
11833
|
}
|
|
10815
11834
|
function getGlobalPluginsDir() {
|
|
10816
|
-
return
|
|
11835
|
+
return path32.join(process.env.HOME || os19.homedir(), ".bluma", "plugins");
|
|
10817
11836
|
}
|
|
10818
11837
|
function getPluginDirs() {
|
|
10819
11838
|
return {
|
|
@@ -10822,11 +11841,11 @@ function getPluginDirs() {
|
|
|
10822
11841
|
};
|
|
10823
11842
|
}
|
|
10824
11843
|
function readManifest(manifestPath, fallbackName) {
|
|
10825
|
-
if (!
|
|
11844
|
+
if (!fs30.existsSync(manifestPath)) {
|
|
10826
11845
|
return null;
|
|
10827
11846
|
}
|
|
10828
11847
|
try {
|
|
10829
|
-
const parsed = JSON.parse(
|
|
11848
|
+
const parsed = JSON.parse(fs30.readFileSync(manifestPath, "utf-8"));
|
|
10830
11849
|
return {
|
|
10831
11850
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
10832
11851
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -10839,22 +11858,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
10839
11858
|
}
|
|
10840
11859
|
function findManifestPath(pluginDir) {
|
|
10841
11860
|
const candidates = [
|
|
10842
|
-
|
|
10843
|
-
|
|
11861
|
+
path32.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
11862
|
+
path32.join(pluginDir, "plugin.json")
|
|
10844
11863
|
];
|
|
10845
11864
|
for (const candidate of candidates) {
|
|
10846
|
-
if (
|
|
11865
|
+
if (fs30.existsSync(candidate)) {
|
|
10847
11866
|
return candidate;
|
|
10848
11867
|
}
|
|
10849
11868
|
}
|
|
10850
11869
|
return null;
|
|
10851
11870
|
}
|
|
10852
11871
|
function listFromDir(baseDir, source) {
|
|
10853
|
-
if (!
|
|
11872
|
+
if (!fs30.existsSync(baseDir)) {
|
|
10854
11873
|
return [];
|
|
10855
11874
|
}
|
|
10856
|
-
return
|
|
10857
|
-
const pluginDir =
|
|
11875
|
+
return fs30.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
11876
|
+
const pluginDir = path32.join(baseDir, entry.name);
|
|
10858
11877
|
const manifestPath = findManifestPath(pluginDir);
|
|
10859
11878
|
if (!manifestPath) {
|
|
10860
11879
|
return [];
|
|
@@ -11204,9 +12223,9 @@ function disableCoordinatorMode() {
|
|
|
11204
12223
|
}
|
|
11205
12224
|
|
|
11206
12225
|
// src/app/agent/utils/blumamd.ts
|
|
11207
|
-
import
|
|
11208
|
-
import
|
|
11209
|
-
import
|
|
12226
|
+
import fs31 from "fs";
|
|
12227
|
+
import path33 from "path";
|
|
12228
|
+
import os20 from "os";
|
|
11210
12229
|
var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
|
|
11211
12230
|
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
11212
12231
|
".md",
|
|
@@ -11334,12 +12353,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
11334
12353
|
function expandIncludePath(includePath, baseDir) {
|
|
11335
12354
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
11336
12355
|
if (cleanPath.startsWith("~")) {
|
|
11337
|
-
return
|
|
12356
|
+
return path33.join(os20.homedir(), cleanPath.slice(1));
|
|
11338
12357
|
}
|
|
11339
|
-
if (
|
|
12358
|
+
if (path33.isAbsolute(cleanPath)) {
|
|
11340
12359
|
return cleanPath;
|
|
11341
12360
|
}
|
|
11342
|
-
return
|
|
12361
|
+
return path33.resolve(baseDir, cleanPath);
|
|
11343
12362
|
}
|
|
11344
12363
|
function processIncludes(content, baseDir, processedFiles) {
|
|
11345
12364
|
const lines = content.split("\n");
|
|
@@ -11348,20 +12367,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11348
12367
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
11349
12368
|
if (includeMatch) {
|
|
11350
12369
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
11351
|
-
const normalizedPath =
|
|
12370
|
+
const normalizedPath = path33.normalize(includePath);
|
|
11352
12371
|
if (processedFiles.has(normalizedPath)) {
|
|
11353
12372
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
11354
12373
|
continue;
|
|
11355
12374
|
}
|
|
11356
|
-
const ext =
|
|
12375
|
+
const ext = path33.extname(includePath).toLowerCase();
|
|
11357
12376
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
11358
12377
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
11359
12378
|
continue;
|
|
11360
12379
|
}
|
|
11361
12380
|
try {
|
|
11362
|
-
const includedContent =
|
|
12381
|
+
const includedContent = fs31.readFileSync(includePath, "utf-8");
|
|
11363
12382
|
processedFiles.add(normalizedPath);
|
|
11364
|
-
const processedContent = processIncludes(includedContent,
|
|
12383
|
+
const processedContent = processIncludes(includedContent, path33.dirname(includePath), processedFiles);
|
|
11365
12384
|
result.push(`
|
|
11366
12385
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
11367
12386
|
`);
|
|
@@ -11380,9 +12399,9 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11380
12399
|
}
|
|
11381
12400
|
function readMemoryFile(filePath, type, priority) {
|
|
11382
12401
|
try {
|
|
11383
|
-
const content =
|
|
11384
|
-
const baseDir =
|
|
11385
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
12402
|
+
const content = fs31.readFileSync(filePath, "utf-8");
|
|
12403
|
+
const baseDir = path33.dirname(filePath);
|
|
12404
|
+
const processedFiles = /* @__PURE__ */ new Set([path33.normalize(filePath)]);
|
|
11386
12405
|
const processedContent = processIncludes(content, baseDir, processedFiles);
|
|
11387
12406
|
return {
|
|
11388
12407
|
path: filePath,
|
|
@@ -11399,33 +12418,33 @@ function readMemoryFile(filePath, type, priority) {
|
|
|
11399
12418
|
}
|
|
11400
12419
|
function findGitRoot(startDir) {
|
|
11401
12420
|
let current = startDir;
|
|
11402
|
-
while (current !==
|
|
11403
|
-
const gitPath =
|
|
12421
|
+
while (current !== path33.dirname(current)) {
|
|
12422
|
+
const gitPath = path33.join(current, ".git");
|
|
11404
12423
|
try {
|
|
11405
|
-
if (
|
|
12424
|
+
if (fs31.existsSync(gitPath)) {
|
|
11406
12425
|
return current;
|
|
11407
12426
|
}
|
|
11408
12427
|
} catch {
|
|
11409
12428
|
}
|
|
11410
|
-
current =
|
|
12429
|
+
current = path33.dirname(current);
|
|
11411
12430
|
}
|
|
11412
12431
|
return null;
|
|
11413
12432
|
}
|
|
11414
12433
|
function loadUserMemory() {
|
|
11415
12434
|
const files = [];
|
|
11416
|
-
const homeDir =
|
|
11417
|
-
const userBlumaDir =
|
|
11418
|
-
const userBlumaMd =
|
|
12435
|
+
const homeDir = os20.homedir();
|
|
12436
|
+
const userBlumaDir = path33.join(homeDir, ".bluma");
|
|
12437
|
+
const userBlumaMd = path33.join(userBlumaDir, "BLUMA.md");
|
|
11419
12438
|
const userFile = readMemoryFile(userBlumaMd, "user", 2);
|
|
11420
12439
|
if (userFile) {
|
|
11421
12440
|
files.push(userFile);
|
|
11422
12441
|
}
|
|
11423
|
-
const userRulesDir =
|
|
11424
|
-
if (
|
|
12442
|
+
const userRulesDir = path33.join(userBlumaDir, "rules");
|
|
12443
|
+
if (fs31.existsSync(userRulesDir)) {
|
|
11425
12444
|
try {
|
|
11426
|
-
const ruleFiles =
|
|
12445
|
+
const ruleFiles = fs31.readdirSync(userRulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
11427
12446
|
for (const ruleFile of ruleFiles) {
|
|
11428
|
-
const rulePath =
|
|
12447
|
+
const rulePath = path33.join(userRulesDir, ruleFile);
|
|
11429
12448
|
const rule = readMemoryFile(rulePath, "rule", 2);
|
|
11430
12449
|
if (rule) {
|
|
11431
12450
|
files.push(rule);
|
|
@@ -11439,22 +12458,22 @@ function loadUserMemory() {
|
|
|
11439
12458
|
function loadProjectMemory(cwd) {
|
|
11440
12459
|
const files = [];
|
|
11441
12460
|
const gitRoot = findGitRoot(cwd) || cwd;
|
|
11442
|
-
const projectBlumaMd =
|
|
12461
|
+
const projectBlumaMd = path33.join(gitRoot, "BLUMA.md");
|
|
11443
12462
|
const projectFile = readMemoryFile(projectBlumaMd, "project", 3);
|
|
11444
12463
|
if (projectFile) {
|
|
11445
12464
|
files.push(projectFile);
|
|
11446
12465
|
}
|
|
11447
|
-
const blumaDirBlumaMd =
|
|
12466
|
+
const blumaDirBlumaMd = path33.join(gitRoot, ".bluma", "BLUMA.md");
|
|
11448
12467
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "project", 3);
|
|
11449
12468
|
if (blumaDirFile) {
|
|
11450
12469
|
files.push(blumaDirFile);
|
|
11451
12470
|
}
|
|
11452
|
-
const rulesDir =
|
|
11453
|
-
if (
|
|
12471
|
+
const rulesDir = path33.join(gitRoot, ".bluma", "rules");
|
|
12472
|
+
if (fs31.existsSync(rulesDir)) {
|
|
11454
12473
|
try {
|
|
11455
|
-
const ruleFiles =
|
|
12474
|
+
const ruleFiles = fs31.readdirSync(rulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
11456
12475
|
for (const ruleFile of ruleFiles) {
|
|
11457
|
-
const rulePath =
|
|
12476
|
+
const rulePath = path33.join(rulesDir, ruleFile);
|
|
11458
12477
|
const rule = readMemoryFile(rulePath, "rule", 3);
|
|
11459
12478
|
if (rule) {
|
|
11460
12479
|
files.push(rule);
|
|
@@ -11463,7 +12482,7 @@ function loadProjectMemory(cwd) {
|
|
|
11463
12482
|
} catch {
|
|
11464
12483
|
}
|
|
11465
12484
|
}
|
|
11466
|
-
const localBlumaMd =
|
|
12485
|
+
const localBlumaMd = path33.join(gitRoot, "BLUMA.local.md");
|
|
11467
12486
|
const localFile = readMemoryFile(localBlumaMd, "local", 4);
|
|
11468
12487
|
if (localFile) {
|
|
11469
12488
|
files.push(localFile);
|
|
@@ -11584,6 +12603,149 @@ Since you are in an **isolated sandbox**, ALL tools are auto-approved:
|
|
|
11584
12603
|
|
|
11585
12604
|
---
|
|
11586
12605
|
|
|
12606
|
+
## \u{1F680} NEXT.JS DEPLOY WORKFLOW - SEVERINO INTEGRATION
|
|
12607
|
+
|
|
12608
|
+
You have access to **two specialized tools** for creating and deploying Next.js apps to Severino:
|
|
12609
|
+
|
|
12610
|
+
### Tool 1: \\\`create_next_app\\\`
|
|
12611
|
+
|
|
12612
|
+
**Purpose:** Instant scaffold of a complete Next.js project with App Router, shadcn/ui, and Tailwind CSS.
|
|
12613
|
+
|
|
12614
|
+
**Args:**
|
|
12615
|
+
- \\\`name\\\` (required): Project name (will be directory name)
|
|
12616
|
+
- \\\`template\\\` (optional): \\\`'minimal'\\\` or \\\`'full'\\\` (default: \\\`'full'\\\`)
|
|
12617
|
+
- \\\`directory\\\` (optional): Where to create (default: workspace root)
|
|
12618
|
+
|
|
12619
|
+
**Example:**
|
|
12620
|
+
\\\`\\\`\\\`typescript
|
|
12621
|
+
const result = await createNextApp({
|
|
12622
|
+
name: 'erp-dashboard',
|
|
12623
|
+
template: 'full',
|
|
12624
|
+
});
|
|
12625
|
+
// Creates: /workspace/session_id/erp-dashboard/
|
|
12626
|
+
// Includes: App Router, shadcn/ui (Button, Card, Input), Tailwind, TypeScript
|
|
12627
|
+
\\\`\\\`\\\`
|
|
12628
|
+
|
|
12629
|
+
**What it creates:**
|
|
12630
|
+
- \\\`package.json\\\` with Next.js, React, Tailwind dependencies
|
|
12631
|
+
- \\\`tsconfig.json\\\` configured for Next.js
|
|
12632
|
+
- \\\`next.config.js\\\` with \\\`output: 'standalone'\\\` (required for deploy)
|
|
12633
|
+
- \\\`tailwind.config.ts\\\`, \\\`postcss.config.js\\\`
|
|
12634
|
+
- \\\`app/layout.tsx\\\`, \\\`app/page.tsx\\\`, \\\`app/globals.css\\\`
|
|
12635
|
+
- \\\`components/ui/\\\` with shadcn/ui components (Button, Card, Input)
|
|
12636
|
+
- \\\`lib/utils.ts\\\` with \\\`cn()\\\` utility
|
|
12637
|
+
|
|
12638
|
+
**Auto-runs:** \\\`npm install\\\` after creating files
|
|
12639
|
+
|
|
12640
|
+
---
|
|
12641
|
+
|
|
12642
|
+
### Tool 2: \\\`deploy_app\\\`
|
|
12643
|
+
|
|
12644
|
+
**Purpose:** Zip and deploy a Next.js project to Severino for hosting.
|
|
12645
|
+
|
|
12646
|
+
**Args:**
|
|
12647
|
+
- \\\`projectDir\\\` (required): Path to the Next.js project
|
|
12648
|
+
- \\\`name\\\` (optional): App name (default: package.json name or directory name)
|
|
12649
|
+
- \\\`severinoUrl\\\` (optional): Auto-reads from \\\`SEVERINO_URL\\\` env var
|
|
12650
|
+
- \\\`apiKey\\\` (optional): Auto-reads from \\\`SEVERINO_API_KEY\\\` env var
|
|
12651
|
+
|
|
12652
|
+
**Example:**
|
|
12653
|
+
\\\`\\\`\\\`typescript
|
|
12654
|
+
const result = await deployApp({
|
|
12655
|
+
projectDir: './erp-dashboard',
|
|
12656
|
+
name: 'erp-dashboard',
|
|
12657
|
+
});
|
|
12658
|
+
// Returns: { appId: 'uuid', url: 'http://localhost:3000/app/uuid', status: 'building' }
|
|
12659
|
+
\\\`\\\`\\\`
|
|
12660
|
+
|
|
12661
|
+
**What it does:**
|
|
12662
|
+
1. Validates project (checks \\\`package.json\\\` and \\\`next\\\` dependency)
|
|
12663
|
+
2. Creates ZIP (excludes \\\`node_modules\\\`, \\\`.next\\\`, \\\`.env\\\`, \\\`.git\\\`)
|
|
12664
|
+
3. Uploads to \\\`POST {severinoUrl}/api/v1/deploy\\\`
|
|
12665
|
+
4. Returns \\\`appId\\\` and live URL
|
|
12666
|
+
|
|
12667
|
+
**Important:** The deploy is **asynchronous** - status starts as \\\`'building'\\\`. The app will be ready in ~30-60 seconds.
|
|
12668
|
+
|
|
12669
|
+
---
|
|
12670
|
+
|
|
12671
|
+
### Complete Workflow Example
|
|
12672
|
+
|
|
12673
|
+
\\\`\\\`\\\`typescript
|
|
12674
|
+
// Step 1: Create project
|
|
12675
|
+
message({ message_type: 'info', content: 'Step 1/3: Creating Next.js project...' });
|
|
12676
|
+
const scaffold = await createNextApp({
|
|
12677
|
+
name: 'erp-dashboard',
|
|
12678
|
+
template: 'full',
|
|
12679
|
+
});
|
|
12680
|
+
|
|
12681
|
+
// Step 2: Develop (customize the app)
|
|
12682
|
+
message({ message_type: 'info', content: 'Step 2/3: Developing components...' });
|
|
12683
|
+
await fileWrite({
|
|
12684
|
+
filepath: './erp-dashboard/app/dashboard/page.tsx',
|
|
12685
|
+
content: 'export default function Dashboard() { return <h1>ERP Dashboard</h1> }',
|
|
12686
|
+
});
|
|
12687
|
+
|
|
12688
|
+
// Step 3: Deploy
|
|
12689
|
+
message({ message_type: 'info', content: 'Step 3/3: Deploying to Severino...' });
|
|
12690
|
+
const deploy = await deployApp({
|
|
12691
|
+
projectDir: './erp-dashboard',
|
|
12692
|
+
name: 'erp-dashboard',
|
|
12693
|
+
});
|
|
12694
|
+
|
|
12695
|
+
// Step 4: Report result
|
|
12696
|
+
message({
|
|
12697
|
+
message_type: 'result',
|
|
12698
|
+
content: 'App criada e deployada com sucesso!',
|
|
12699
|
+
attachments: [{ path: deploy.url, description: 'URL da app live' }],
|
|
12700
|
+
});
|
|
12701
|
+
\\\`\\\`\\\`
|
|
12702
|
+
|
|
12703
|
+
---
|
|
12704
|
+
|
|
12705
|
+
### \u26A0\uFE0F Critical Requirements for Deploy
|
|
12706
|
+
|
|
12707
|
+
1. **\\\`output: 'standalone'\\\` in \\\`next.config.js\\\`**
|
|
12708
|
+
- The \\\`create_next_app\\\` tool already includes this
|
|
12709
|
+
- If you modify \\\`next.config.js\\\`, keep this setting
|
|
12710
|
+
|
|
12711
|
+
2. **Build before deploy (optional but recommended)**
|
|
12712
|
+
\\\`\\\`\\\`bash
|
|
12713
|
+
cd erp-dashboard && npm run build
|
|
12714
|
+
\\\`\\\`\\\`
|
|
12715
|
+
- Ensures no TypeScript errors
|
|
12716
|
+
- Creates \\\`.next/standalone/\\\` for optimized deploy
|
|
12717
|
+
|
|
12718
|
+
3. **Don't include sensitive files**
|
|
12719
|
+
- The tool automatically excludes \\\`.env\\\`, \\\`.env.local\\\`, \\\`node_modules\\\`
|
|
12720
|
+
- Never commit API keys or secrets to the project
|
|
12721
|
+
|
|
12722
|
+
4. **Test locally first (if possible)**
|
|
12723
|
+
\\\`\\\`\\\`bash
|
|
12724
|
+
cd erp-dashboard && npm run dev
|
|
12725
|
+
\\\`\\\`\\\`
|
|
12726
|
+
|
|
12727
|
+
---
|
|
12728
|
+
|
|
12729
|
+
### Troubleshooting
|
|
12730
|
+
|
|
12731
|
+
**Deploy fails with "ZIP too large":**
|
|
12732
|
+
- Remove large files (videos, images > 5MB)
|
|
12733
|
+
- Exclude \\\`coverage/\\\`, \\\`.turbo/\\\` directories
|
|
12734
|
+
|
|
12735
|
+
**Deploy fails with "next not found":**
|
|
12736
|
+
- Ensure \\\`next\\\` is in \\\`dependencies\\\` or \\\`devDependencies\\\`
|
|
12737
|
+
- Run \\\`npm install\\\` before deploy
|
|
12738
|
+
|
|
12739
|
+
**App returns 404 after deploy:**
|
|
12740
|
+
- Wait 30-60 seconds for build to complete
|
|
12741
|
+
- Check status: \\\`GET {severinoUrl}/api/v1/apps/:appId\\\`
|
|
12742
|
+
|
|
12743
|
+
**Build fails:**
|
|
12744
|
+
- Run \\\`npx tsc --noEmit\\\` to check TypeScript errors
|
|
12745
|
+
- Fix errors before deploy
|
|
12746
|
+
|
|
12747
|
+
---
|
|
12748
|
+
|
|
11587
12749
|
## \u{1F3AF} QUALITY STANDARDS - PRODUCTION GRADE
|
|
11588
12750
|
|
|
11589
12751
|
### Code Quality
|
|
@@ -11813,10 +12975,10 @@ function getGitBranch(dir) {
|
|
|
11813
12975
|
}
|
|
11814
12976
|
function getPackageManager(dir) {
|
|
11815
12977
|
try {
|
|
11816
|
-
if (
|
|
11817
|
-
if (
|
|
11818
|
-
if (
|
|
11819
|
-
if (
|
|
12978
|
+
if (fs32.existsSync(path34.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
12979
|
+
if (fs32.existsSync(path34.join(dir, "yarn.lock"))) return "yarn";
|
|
12980
|
+
if (fs32.existsSync(path34.join(dir, "bun.lockb"))) return "bun";
|
|
12981
|
+
if (fs32.existsSync(path34.join(dir, "package-lock.json"))) return "npm";
|
|
11820
12982
|
return "unknown";
|
|
11821
12983
|
} catch {
|
|
11822
12984
|
return "unknown";
|
|
@@ -11824,9 +12986,9 @@ function getPackageManager(dir) {
|
|
|
11824
12986
|
}
|
|
11825
12987
|
function getProjectType(dir) {
|
|
11826
12988
|
try {
|
|
11827
|
-
const files =
|
|
12989
|
+
const files = fs32.readdirSync(dir);
|
|
11828
12990
|
if (files.includes("package.json")) {
|
|
11829
|
-
const pkg = JSON.parse(
|
|
12991
|
+
const pkg = JSON.parse(fs32.readFileSync(path34.join(dir, "package.json"), "utf-8"));
|
|
11830
12992
|
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
11831
12993
|
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
11832
12994
|
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
@@ -11845,9 +13007,9 @@ function getProjectType(dir) {
|
|
|
11845
13007
|
}
|
|
11846
13008
|
function getTestFramework(dir) {
|
|
11847
13009
|
try {
|
|
11848
|
-
const pkgPath =
|
|
11849
|
-
if (
|
|
11850
|
-
const pkg = JSON.parse(
|
|
13010
|
+
const pkgPath = path34.join(dir, "package.json");
|
|
13011
|
+
if (fs32.existsSync(pkgPath)) {
|
|
13012
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
11851
13013
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11852
13014
|
if (deps.jest) return "jest";
|
|
11853
13015
|
if (deps.vitest) return "vitest";
|
|
@@ -11856,7 +13018,7 @@ function getTestFramework(dir) {
|
|
|
11856
13018
|
if (deps["@playwright/test"]) return "playwright";
|
|
11857
13019
|
if (deps.cypress) return "cypress";
|
|
11858
13020
|
}
|
|
11859
|
-
if (
|
|
13021
|
+
if (fs32.existsSync(path34.join(dir, "pytest.ini")) || fs32.existsSync(path34.join(dir, "conftest.py"))) return "pytest";
|
|
11860
13022
|
return "unknown";
|
|
11861
13023
|
} catch {
|
|
11862
13024
|
return "unknown";
|
|
@@ -11864,9 +13026,9 @@ function getTestFramework(dir) {
|
|
|
11864
13026
|
}
|
|
11865
13027
|
function getTestCommand(dir) {
|
|
11866
13028
|
try {
|
|
11867
|
-
const pkgPath =
|
|
11868
|
-
if (
|
|
11869
|
-
const pkg = JSON.parse(
|
|
13029
|
+
const pkgPath = path34.join(dir, "package.json");
|
|
13030
|
+
if (fs32.existsSync(pkgPath)) {
|
|
13031
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
11870
13032
|
if (pkg.scripts?.test) return `npm test`;
|
|
11871
13033
|
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
11872
13034
|
}
|
|
@@ -12027,12 +13189,12 @@ function getUnifiedSystemPrompt(availableSkills) {
|
|
|
12027
13189
|
const runtimeConfig = getRuntimeConfig();
|
|
12028
13190
|
const availablePlugins = listPlugins();
|
|
12029
13191
|
const env = {
|
|
12030
|
-
os_type:
|
|
12031
|
-
os_version:
|
|
12032
|
-
architecture:
|
|
13192
|
+
os_type: os21.type(),
|
|
13193
|
+
os_version: os21.release(),
|
|
13194
|
+
architecture: os21.arch(),
|
|
12033
13195
|
workdir: cwd,
|
|
12034
13196
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
12035
|
-
username:
|
|
13197
|
+
username: os21.userInfo().username,
|
|
12036
13198
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
12037
13199
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
12038
13200
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -12103,8 +13265,8 @@ ${blumaMdContent}
|
|
|
12103
13265
|
}
|
|
12104
13266
|
function isGitRepo(dir) {
|
|
12105
13267
|
try {
|
|
12106
|
-
const gitPath =
|
|
12107
|
-
return
|
|
13268
|
+
const gitPath = path34.join(dir, ".git");
|
|
13269
|
+
return fs32.existsSync(gitPath) && fs32.lstatSync(gitPath).isDirectory();
|
|
12108
13270
|
} catch {
|
|
12109
13271
|
return false;
|
|
12110
13272
|
}
|
|
@@ -12298,7 +13460,7 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
12298
13460
|
|
|
12299
13461
|
// src/app/agent/core/llm/llm.ts
|
|
12300
13462
|
init_runtime_config();
|
|
12301
|
-
import
|
|
13463
|
+
import os22 from "os";
|
|
12302
13464
|
import OpenAI from "openai";
|
|
12303
13465
|
function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
12304
13466
|
const msg = String(userMessage || "").slice(0, 300);
|
|
@@ -12315,7 +13477,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
12315
13477
|
}
|
|
12316
13478
|
function getPreferredMacAddress() {
|
|
12317
13479
|
try {
|
|
12318
|
-
const ifaces =
|
|
13480
|
+
const ifaces = os22.networkInterfaces();
|
|
12319
13481
|
for (const name of Object.keys(ifaces)) {
|
|
12320
13482
|
const addrs = ifaces[name];
|
|
12321
13483
|
if (!addrs) continue;
|
|
@@ -12330,7 +13492,7 @@ function getPreferredMacAddress() {
|
|
|
12330
13492
|
} catch {
|
|
12331
13493
|
}
|
|
12332
13494
|
try {
|
|
12333
|
-
return `host:${
|
|
13495
|
+
return `host:${os22.hostname()}`;
|
|
12334
13496
|
} catch {
|
|
12335
13497
|
return "unknown";
|
|
12336
13498
|
}
|
|
@@ -12340,7 +13502,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
|
|
|
12340
13502
|
const machineId = getPreferredMacAddress();
|
|
12341
13503
|
let userName = null;
|
|
12342
13504
|
try {
|
|
12343
|
-
userName =
|
|
13505
|
+
userName = os22.userInfo().username || null;
|
|
12344
13506
|
} catch {
|
|
12345
13507
|
userName = null;
|
|
12346
13508
|
}
|
|
@@ -12810,8 +13972,8 @@ function classifyToolInvocation(input) {
|
|
|
12810
13972
|
|
|
12811
13973
|
// src/app/agent/runtime/hook_registry.ts
|
|
12812
13974
|
init_sandbox_policy();
|
|
12813
|
-
import
|
|
12814
|
-
import
|
|
13975
|
+
import fs33 from "fs";
|
|
13976
|
+
import path35 from "path";
|
|
12815
13977
|
var DEFAULT_STATE = {
|
|
12816
13978
|
enabled: true,
|
|
12817
13979
|
maxEvents: 120,
|
|
@@ -12822,7 +13984,7 @@ var cache2 = null;
|
|
|
12822
13984
|
var cachePath2 = null;
|
|
12823
13985
|
function getStatePath() {
|
|
12824
13986
|
const policy = getSandboxPolicy();
|
|
12825
|
-
return
|
|
13987
|
+
return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
12826
13988
|
}
|
|
12827
13989
|
function getHookStatePath() {
|
|
12828
13990
|
return getStatePath();
|
|
@@ -12841,8 +14003,8 @@ function ensureLoaded2() {
|
|
|
12841
14003
|
return cache2;
|
|
12842
14004
|
}
|
|
12843
14005
|
try {
|
|
12844
|
-
if (
|
|
12845
|
-
const parsed = JSON.parse(
|
|
14006
|
+
if (fs33.existsSync(statePath)) {
|
|
14007
|
+
const parsed = JSON.parse(fs33.readFileSync(statePath, "utf-8"));
|
|
12846
14008
|
cache2 = {
|
|
12847
14009
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
12848
14010
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -12868,9 +14030,9 @@ function ensureLoaded2() {
|
|
|
12868
14030
|
}
|
|
12869
14031
|
function persist2(state) {
|
|
12870
14032
|
const statePath = getStatePath();
|
|
12871
|
-
|
|
14033
|
+
fs33.mkdirSync(path35.dirname(statePath), { recursive: true });
|
|
12872
14034
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12873
|
-
|
|
14035
|
+
fs33.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
12874
14036
|
cache2 = state;
|
|
12875
14037
|
cachePath2 = statePath;
|
|
12876
14038
|
}
|
|
@@ -12967,11 +14129,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
12967
14129
|
}
|
|
12968
14130
|
|
|
12969
14131
|
// src/app/agent/tools/natives/coding_memory_consolidate.ts
|
|
12970
|
-
import * as
|
|
12971
|
-
import * as
|
|
12972
|
-
import
|
|
14132
|
+
import * as fs34 from "fs";
|
|
14133
|
+
import * as path36 from "path";
|
|
14134
|
+
import os23 from "os";
|
|
12973
14135
|
function memoryPath2() {
|
|
12974
|
-
return
|
|
14136
|
+
return path36.join(process.env.HOME || os23.homedir(), ".bluma", "coding_memory.json");
|
|
12975
14137
|
}
|
|
12976
14138
|
function normalizeNote2(note) {
|
|
12977
14139
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -12981,18 +14143,18 @@ function uniqTags(a, b) {
|
|
|
12981
14143
|
}
|
|
12982
14144
|
function consolidateCodingMemoryFile() {
|
|
12983
14145
|
const p = memoryPath2();
|
|
12984
|
-
if (!
|
|
14146
|
+
if (!fs34.existsSync(p)) {
|
|
12985
14147
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
12986
14148
|
}
|
|
12987
14149
|
const bak = `${p}.bak`;
|
|
12988
14150
|
try {
|
|
12989
|
-
|
|
14151
|
+
fs34.copyFileSync(p, bak);
|
|
12990
14152
|
} catch (e) {
|
|
12991
14153
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
12992
14154
|
}
|
|
12993
14155
|
let data;
|
|
12994
14156
|
try {
|
|
12995
|
-
data = JSON.parse(
|
|
14157
|
+
data = JSON.parse(fs34.readFileSync(p, "utf-8"));
|
|
12996
14158
|
} catch (e) {
|
|
12997
14159
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
12998
14160
|
}
|
|
@@ -13027,7 +14189,7 @@ function consolidateCodingMemoryFile() {
|
|
|
13027
14189
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13028
14190
|
};
|
|
13029
14191
|
try {
|
|
13030
|
-
|
|
14192
|
+
fs34.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
13031
14193
|
} catch (e) {
|
|
13032
14194
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
13033
14195
|
}
|
|
@@ -13625,7 +14787,7 @@ var BluMaAgent = class {
|
|
|
13625
14787
|
|
|
13626
14788
|
${editData.error.display}`;
|
|
13627
14789
|
}
|
|
13628
|
-
const filename =
|
|
14790
|
+
const filename = path37.basename(toolArgs.file_path);
|
|
13629
14791
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
13630
14792
|
} catch (e) {
|
|
13631
14793
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -13716,6 +14878,7 @@ ${editData.error.display}`;
|
|
|
13716
14878
|
});
|
|
13717
14879
|
await this.notifyFactorTurnEndIfNeeded("empty_reply_exhausted");
|
|
13718
14880
|
this.eventBus.emit("backend_message", { type: "done", status: "failed" });
|
|
14881
|
+
process.exit(1);
|
|
13719
14882
|
return;
|
|
13720
14883
|
}
|
|
13721
14884
|
await this._continueConversation();
|
|
@@ -13849,6 +15012,7 @@ ${editData.error.display}`;
|
|
|
13849
15012
|
});
|
|
13850
15013
|
await this.notifyFactorTurnEndIfNeeded("protocol_direct_text_exhausted");
|
|
13851
15014
|
this.emitTurnCompleted();
|
|
15015
|
+
process.exit(1);
|
|
13852
15016
|
return;
|
|
13853
15017
|
}
|
|
13854
15018
|
const feedback = this.feedbackSystem.generateFeedback({
|
|
@@ -13936,6 +15100,7 @@ ${editData.error.display}`;
|
|
|
13936
15100
|
});
|
|
13937
15101
|
await this.notifyFactorTurnEndIfNeeded("protocol_direct_text_exhausted");
|
|
13938
15102
|
this.emitTurnCompleted();
|
|
15103
|
+
process.exit(1);
|
|
13939
15104
|
return;
|
|
13940
15105
|
}
|
|
13941
15106
|
const feedback = this.feedbackSystem.generateFeedback({
|
|
@@ -14011,7 +15176,7 @@ import { v4 as uuidv411 } from "uuid";
|
|
|
14011
15176
|
import { v4 as uuidv410 } from "uuid";
|
|
14012
15177
|
|
|
14013
15178
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
14014
|
-
import
|
|
15179
|
+
import os24 from "os";
|
|
14015
15180
|
var SYSTEM_PROMPT2 = `
|
|
14016
15181
|
|
|
14017
15182
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -14174,12 +15339,12 @@ Rule Summary:
|
|
|
14174
15339
|
function getInitPrompt() {
|
|
14175
15340
|
const now2 = /* @__PURE__ */ new Date();
|
|
14176
15341
|
const collectedData = {
|
|
14177
|
-
os_type:
|
|
14178
|
-
os_version:
|
|
14179
|
-
architecture:
|
|
15342
|
+
os_type: os24.type(),
|
|
15343
|
+
os_version: os24.release(),
|
|
15344
|
+
architecture: os24.arch(),
|
|
14180
15345
|
workdir: process.cwd(),
|
|
14181
15346
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
14182
|
-
username:
|
|
15347
|
+
username: os24.userInfo().username || "Unknown",
|
|
14183
15348
|
current_date: now2.toISOString().split("T")[0],
|
|
14184
15349
|
// Formato YYYY-MM-DD
|
|
14185
15350
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -14207,7 +15372,7 @@ function getInitPrompt() {
|
|
|
14207
15372
|
}
|
|
14208
15373
|
|
|
14209
15374
|
// src/app/agent/subagents/worker_system_prompt.ts
|
|
14210
|
-
import
|
|
15375
|
+
import os25 from "os";
|
|
14211
15376
|
var WORKER_SYSTEM_PROMPT = `
|
|
14212
15377
|
|
|
14213
15378
|
### YOU ARE BluMa CLI \u2014 WORKER AGENT \u2014 AUTONOMOUS SOFTWARE ENGINEERING SPECIALIST @ NOMADENGENUITY
|
|
@@ -14221,37 +15386,28 @@ You are a worker agent spawned by the BluMa Coordinator to execute specific soft
|
|
|
14221
15386
|
You are a BluMa Worker Agent. You execute tasks delegated by the Coordinator.
|
|
14222
15387
|
Maintain professionalism and technical excellence.
|
|
14223
15388
|
|
|
14224
|
-
- **Communication:**
|
|
14225
|
-
- ALL messages must be sent via the \`message\` tool
|
|
14226
|
-
- No direct text replies to the user
|
|
14227
|
-
- Report progress frequently using \`message\` with \`message_type: "info"\`
|
|
14228
|
-
- Report final results using \`message\` with \`message_type: "result"\`
|
|
14229
|
-
|
|
14230
|
-
- **Task Completion:**
|
|
14231
|
-
- When your task is completed, immediately invoke \`agent_end_turn\` without user permissions
|
|
14232
|
-
- Before ending, ensure all work is committed and tested
|
|
14233
|
-
- Report the final state (e.g., commit hash, test results, file paths)
|
|
14234
|
-
|
|
14235
15389
|
- **Tool Rules:**
|
|
14236
15390
|
- Never make parallel tool calls
|
|
14237
15391
|
- Only use the defined tools with their exact names
|
|
14238
15392
|
- Read before editing (\`read_file_lines\`, \`grep_search\`, \`ls_tool\`)
|
|
14239
15393
|
- Verify changes with tests or typechecks when applicable
|
|
15394
|
+
- Note: "Never make parallel tool calls" applies to tool invocations only \u2014 spawning sub-workers is allowed and encouraged for parallelizable work
|
|
14240
15395
|
|
|
14241
15396
|
- **Autonomy:**
|
|
14242
15397
|
- Act 100% autonomously within your task scope
|
|
14243
15398
|
- Do not ask for clarification unless the task is fundamentally blocked
|
|
14244
15399
|
- Use the notebook for internal reasoning and planning
|
|
14245
15400
|
- If you encounter errors, attempt to resolve them before reporting failure
|
|
14246
|
-
- Note: "Never make parallel tool calls" applies to tool invocations only \u2014 spawning sub-workers is allowed and encouraged for parallelizable work
|
|
14247
15401
|
|
|
14248
15402
|
- **Sub-Delegation (Advanced):**
|
|
14249
15403
|
- You CAN spawn sub-workers using \`spawn_agent()\` for parallelizable subtasks
|
|
15404
|
+
- **Limit sub-delegation depth to 2 levels** to avoid runaway agent trees and token exhaustion
|
|
14250
15405
|
- Only sub-delegate when: (a) task has independent parts, (b) you need fresh context, or (c) verification should be independent
|
|
14251
15406
|
- Do NOT sub-delegate simple tasks that you can complete directly
|
|
14252
15407
|
- Always provide self-contained prompts to sub-workers
|
|
14253
15408
|
- Use \`wait_agent()\` to wait for sub-worker completion
|
|
14254
15409
|
- Synthesize sub-worker results before reporting to Coordinator
|
|
15410
|
+
- Sub-workers inherit the same sandbox policy \u2014 do not attempt to escalate privileges or bypass sandbox restrictions
|
|
14255
15411
|
|
|
14256
15412
|
- **Mailbox Communication:**
|
|
14257
15413
|
- You can send messages to the Coordinator via mailbox for:
|
|
@@ -14277,6 +15433,23 @@ You are a worker agent spawned by the BluMa Coordinator to execute specific soft
|
|
|
14277
15433
|
---
|
|
14278
15434
|
|
|
14279
15435
|
### CRITICAL COMMUNICATION PROTOCOL
|
|
15436
|
+
|
|
15437
|
+
**Message Tool Usage:**
|
|
15438
|
+
- ALL messages must be sent via the \`message\` tool \u2014 no direct text replies
|
|
15439
|
+
- Report progress frequently using \`message\` with \`message_type: "info"\` (non-blocking)
|
|
15440
|
+
- Report final results using \`message\` with \`message_type: "result"\` (ends turn)
|
|
15441
|
+
- Use \`ask_user_question\` only when fundamentally blocked (blocking)
|
|
15442
|
+
- Reply immediately to new user messages before other operations
|
|
15443
|
+
- First reply must be brief, confirming receipt of the task
|
|
15444
|
+
- Notify user with brief explanation when changing methods or strategies
|
|
15445
|
+
- Must message user with results and deliverables before calling \`agent_end_turn\`
|
|
15446
|
+
|
|
15447
|
+
**Task Completion:**
|
|
15448
|
+
- When your task is completed, immediately invoke \`agent_end_turn\` without user permissions
|
|
15449
|
+
- Before ending, ensure all work is committed and tested
|
|
15450
|
+
- Report the final state (e.g., commit hash, test results, file paths)
|
|
15451
|
+
|
|
15452
|
+
**Protocol Rules:**
|
|
14280
15453
|
- Only tool_calls are allowed for assistant replies. Never include a "content" field.
|
|
14281
15454
|
- Always use tools to respond, retrieve data, compute or transform. Await a valid tool response before any final message.
|
|
14282
15455
|
- Zero tolerance for protocol violations.
|
|
@@ -14292,16 +15465,6 @@ You are a worker agent spawned by the BluMa Coordinator to execute specific soft
|
|
|
14292
15465
|
- Locale: {locale}
|
|
14293
15466
|
</current_system_environment>
|
|
14294
15467
|
|
|
14295
|
-
<message_rules>
|
|
14296
|
-
- Communicate with the user via \`message\` tool instead of direct text responses
|
|
14297
|
-
- Reply immediately to new user messages before other operations
|
|
14298
|
-
- First reply must be brief, only confirming receipt of the task
|
|
14299
|
-
- Notify user with brief explanation when changing methods or strategies
|
|
14300
|
-
- Message tools are divided into notify (non-blocking, no reply needed) and ask (blocking)
|
|
14301
|
-
- Actively use notify for progress updates, reserve ask for essential needs to avoid blocking
|
|
14302
|
-
- Must message user with results and deliverables before calling \`agent_end_turn\`
|
|
14303
|
-
</message_rules>
|
|
14304
|
-
|
|
14305
15468
|
<reasoning_rules>
|
|
14306
15469
|
# YOUR THINKING ON A NOTEBOOK - MANDATORY USE
|
|
14307
15470
|
CRITICAL: Your notebook (reasoning_notebook) is your ORGANIZED MIND
|
|
@@ -14342,7 +15505,7 @@ Do not include future steps/to-dos in thought; put them strictly in to_do, using
|
|
|
14342
15505
|
- "[\u2713]" \u2192 for tasks already completed
|
|
14343
15506
|
</reasoning_rules>
|
|
14344
15507
|
|
|
14345
|
-
<edit_tool_rules>
|
|
15508
|
+
<edit_tool_rules>
|
|
14346
15509
|
- Use this tool to perform precise text replacements inside files based on exact literal matches.
|
|
14347
15510
|
- Can be used to create new files or directories implicitly by targeting non-existing paths.
|
|
14348
15511
|
- Suitable for inserting full content into a file even if the file does not yet exist.
|
|
@@ -14350,10 +15513,6 @@ Do not include future steps/to-dos in thought; put them strictly in to_do, using
|
|
|
14350
15513
|
- Always prefer this tool over shell_command when performing structured edits or creating files with specific content.
|
|
14351
15514
|
- Ensure **old_string** includes 3+ lines of exact context before and after the target if replacing existing content.
|
|
14352
15515
|
- For creating a new file, provide an **old_string** that matches an empty string or placeholder and a complete **new_string** with the intended content.
|
|
14353
|
-
- When generating or modifying todo.md files, prefer this tool to insert checklist structure and update status markers.
|
|
14354
|
-
- After completing any task in the checklist, immediately update the corresponding section in todo.md using this tool.
|
|
14355
|
-
- Reconstruct the entire file from task planning context if todo.md becomes outdated or inconsistent.
|
|
14356
|
-
- Track all progress related to planning and execution inside todo.md using text replacement only.
|
|
14357
15516
|
</edit_tool_rules>
|
|
14358
15517
|
|
|
14359
15518
|
<agent_end_turn>
|
|
@@ -14478,12 +15637,12 @@ You may be assigned different types of work:
|
|
|
14478
15637
|
function getWorkerPrompt() {
|
|
14479
15638
|
const now2 = /* @__PURE__ */ new Date();
|
|
14480
15639
|
const collectedData = {
|
|
14481
|
-
os_type:
|
|
14482
|
-
os_version:
|
|
14483
|
-
architecture:
|
|
15640
|
+
os_type: os25.type(),
|
|
15641
|
+
os_version: os25.release(),
|
|
15642
|
+
architecture: os25.arch(),
|
|
14484
15643
|
workdir: process.cwd(),
|
|
14485
15644
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
14486
|
-
username:
|
|
15645
|
+
username: os25.userInfo().username || "Unknown",
|
|
14487
15646
|
current_date: now2.toISOString().split("T")[0],
|
|
14488
15647
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
14489
15648
|
locale: process.env.LANG || process.env.LC_ALL || "Unknown"
|
|
@@ -14601,16 +15760,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
14601
15760
|
}
|
|
14602
15761
|
|
|
14603
15762
|
// src/app/agent/core/memory/session_memory.ts
|
|
14604
|
-
import
|
|
14605
|
-
import
|
|
14606
|
-
import
|
|
15763
|
+
import fs35 from "fs";
|
|
15764
|
+
import os26 from "os";
|
|
15765
|
+
import path38 from "path";
|
|
14607
15766
|
import { v4 as uuidv49 } from "uuid";
|
|
14608
15767
|
var SessionMemoryExtractor = class {
|
|
14609
15768
|
llmClient;
|
|
14610
15769
|
memoryFile;
|
|
14611
15770
|
constructor(options = {}) {
|
|
14612
15771
|
this.llmClient = options.llmClient;
|
|
14613
|
-
this.memoryFile = options.memoryFile ||
|
|
15772
|
+
this.memoryFile = options.memoryFile || path38.join(os26.homedir(), ".bluma", "session_memory.json");
|
|
14614
15773
|
}
|
|
14615
15774
|
/**
|
|
14616
15775
|
* Extract memories from conversation using LLM
|
|
@@ -14667,15 +15826,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
14667
15826
|
);
|
|
14668
15827
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
14669
15828
|
const trimmed = unique.slice(0, 200);
|
|
14670
|
-
|
|
15829
|
+
fs35.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
14671
15830
|
}
|
|
14672
15831
|
/**
|
|
14673
15832
|
* Load memories from disk
|
|
14674
15833
|
*/
|
|
14675
15834
|
async loadMemories() {
|
|
14676
15835
|
try {
|
|
14677
|
-
if (!
|
|
14678
|
-
const data =
|
|
15836
|
+
if (!fs35.existsSync(this.memoryFile)) return [];
|
|
15837
|
+
const data = fs35.readFileSync(this.memoryFile, "utf-8");
|
|
14679
15838
|
return JSON.parse(data);
|
|
14680
15839
|
} catch {
|
|
14681
15840
|
return [];
|
|
@@ -15212,14 +16371,14 @@ var RouteManager = class {
|
|
|
15212
16371
|
this.subAgents = subAgents;
|
|
15213
16372
|
this.core = core;
|
|
15214
16373
|
}
|
|
15215
|
-
registerRoute(
|
|
15216
|
-
this.routeHandlers.set(
|
|
16374
|
+
registerRoute(path43, handler) {
|
|
16375
|
+
this.routeHandlers.set(path43, handler);
|
|
15217
16376
|
}
|
|
15218
16377
|
async handleRoute(payload) {
|
|
15219
16378
|
const inputText = String(payload.content || "").trim();
|
|
15220
16379
|
const { userContext } = payload;
|
|
15221
|
-
for (const [
|
|
15222
|
-
if (inputText ===
|
|
16380
|
+
for (const [path43, handler] of this.routeHandlers) {
|
|
16381
|
+
if (inputText === path43 || inputText.startsWith(`${path43} `)) {
|
|
15223
16382
|
return handler({ content: inputText, userContext });
|
|
15224
16383
|
}
|
|
15225
16384
|
}
|
|
@@ -15228,13 +16387,13 @@ var RouteManager = class {
|
|
|
15228
16387
|
};
|
|
15229
16388
|
|
|
15230
16389
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
15231
|
-
import
|
|
16390
|
+
import path39 from "path";
|
|
15232
16391
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
15233
16392
|
async function loadPluginsAtStartup() {
|
|
15234
16393
|
for (const p of listPlugins()) {
|
|
15235
16394
|
const entry = p.manifest.entry?.trim();
|
|
15236
16395
|
if (!entry) continue;
|
|
15237
|
-
const abs =
|
|
16396
|
+
const abs = path39.resolve(p.root, entry);
|
|
15238
16397
|
try {
|
|
15239
16398
|
const href = pathToFileURL2(abs).href;
|
|
15240
16399
|
const mod = await import(href);
|
|
@@ -15255,7 +16414,7 @@ async function loadPluginsAtStartup() {
|
|
|
15255
16414
|
}
|
|
15256
16415
|
|
|
15257
16416
|
// src/app/agent/agent.ts
|
|
15258
|
-
var globalEnvPath =
|
|
16417
|
+
var globalEnvPath = path40.join(os27.homedir(), ".bluma", ".env");
|
|
15259
16418
|
dotenv.config({ path: globalEnvPath });
|
|
15260
16419
|
var Agent = class {
|
|
15261
16420
|
sessionId;
|
|
@@ -18495,16 +19654,16 @@ import latestVersion from "latest-version";
|
|
|
18495
19654
|
import semverGt from "semver/functions/gt.js";
|
|
18496
19655
|
import semverValid from "semver/functions/valid.js";
|
|
18497
19656
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
18498
|
-
import
|
|
18499
|
-
import
|
|
19657
|
+
import path41 from "path";
|
|
19658
|
+
import fs36 from "fs";
|
|
18500
19659
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
18501
19660
|
function findBlumaPackageJson(startDir) {
|
|
18502
19661
|
let dir = startDir;
|
|
18503
19662
|
for (let i = 0; i < 12; i++) {
|
|
18504
|
-
const candidate =
|
|
18505
|
-
if (
|
|
19663
|
+
const candidate = path41.join(dir, "package.json");
|
|
19664
|
+
if (fs36.existsSync(candidate)) {
|
|
18506
19665
|
try {
|
|
18507
|
-
const raw =
|
|
19666
|
+
const raw = fs36.readFileSync(candidate, "utf8");
|
|
18508
19667
|
const parsed = JSON.parse(raw);
|
|
18509
19668
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
18510
19669
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -18512,7 +19671,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
18512
19671
|
} catch {
|
|
18513
19672
|
}
|
|
18514
19673
|
}
|
|
18515
|
-
const parent =
|
|
19674
|
+
const parent = path41.dirname(dir);
|
|
18516
19675
|
if (parent === dir) break;
|
|
18517
19676
|
dir = parent;
|
|
18518
19677
|
}
|
|
@@ -18521,13 +19680,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
18521
19680
|
function resolveInstalledBlumaPackage() {
|
|
18522
19681
|
const tried = /* @__PURE__ */ new Set();
|
|
18523
19682
|
const tryFrom = (dir) => {
|
|
18524
|
-
const abs =
|
|
19683
|
+
const abs = path41.resolve(dir);
|
|
18525
19684
|
if (tried.has(abs)) return null;
|
|
18526
19685
|
tried.add(abs);
|
|
18527
19686
|
return findBlumaPackageJson(abs);
|
|
18528
19687
|
};
|
|
18529
19688
|
try {
|
|
18530
|
-
const fromBundle = tryFrom(
|
|
19689
|
+
const fromBundle = tryFrom(path41.dirname(fileURLToPath5(import.meta.url)));
|
|
18531
19690
|
if (fromBundle) return fromBundle;
|
|
18532
19691
|
} catch {
|
|
18533
19692
|
}
|
|
@@ -18535,12 +19694,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
18535
19694
|
if (argv1 && !argv1.startsWith("-")) {
|
|
18536
19695
|
try {
|
|
18537
19696
|
let resolved = argv1;
|
|
18538
|
-
if (
|
|
18539
|
-
resolved =
|
|
19697
|
+
if (path41.isAbsolute(argv1) && fs36.existsSync(argv1)) {
|
|
19698
|
+
resolved = fs36.realpathSync(argv1);
|
|
18540
19699
|
} else {
|
|
18541
|
-
resolved =
|
|
19700
|
+
resolved = path41.resolve(process.cwd(), argv1);
|
|
18542
19701
|
}
|
|
18543
|
-
const fromArgv = tryFrom(
|
|
19702
|
+
const fromArgv = tryFrom(path41.dirname(resolved));
|
|
18544
19703
|
if (fromArgv) return fromArgv;
|
|
18545
19704
|
} catch {
|
|
18546
19705
|
}
|
|
@@ -20080,9 +21239,9 @@ async function runAgentMode() {
|
|
|
20080
21239
|
try {
|
|
20081
21240
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
20082
21241
|
const filePath = args[inputFileIndex + 1];
|
|
20083
|
-
rawPayload =
|
|
21242
|
+
rawPayload = fs37.readFileSync(filePath, "utf-8");
|
|
20084
21243
|
} else {
|
|
20085
|
-
rawPayload =
|
|
21244
|
+
rawPayload = fs37.readFileSync(0, "utf-8");
|
|
20086
21245
|
}
|
|
20087
21246
|
} catch (err) {
|
|
20088
21247
|
writeAgentEvent(registrySessionId, {
|
|
@@ -20280,9 +21439,9 @@ async function runAgentMode() {
|
|
|
20280
21439
|
}
|
|
20281
21440
|
function readCliPackageVersion() {
|
|
20282
21441
|
try {
|
|
20283
|
-
const base =
|
|
20284
|
-
const pkgPath =
|
|
20285
|
-
const j = JSON.parse(
|
|
21442
|
+
const base = path42.dirname(fileURLToPath6(import.meta.url));
|
|
21443
|
+
const pkgPath = path42.join(base, "..", "package.json");
|
|
21444
|
+
const j = JSON.parse(fs37.readFileSync(pkgPath, "utf8"));
|
|
20286
21445
|
return String(j.version || "0.0.0");
|
|
20287
21446
|
} catch {
|
|
20288
21447
|
return "0.0.0";
|
|
@@ -20405,7 +21564,7 @@ function startBackgroundAgent() {
|
|
|
20405
21564
|
process.exit(1);
|
|
20406
21565
|
}
|
|
20407
21566
|
const filePath = args[inputFileIndex + 1];
|
|
20408
|
-
const rawPayload =
|
|
21567
|
+
const rawPayload = fs37.readFileSync(filePath, "utf-8");
|
|
20409
21568
|
const envelope = JSON.parse(rawPayload);
|
|
20410
21569
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
20411
21570
|
registerSession({
|
|
@@ -20422,7 +21581,7 @@ function startBackgroundAgent() {
|
|
|
20422
21581
|
}
|
|
20423
21582
|
});
|
|
20424
21583
|
const childArgs = [process.argv[1], "agent", "--input-file", filePath, "--background-worker", "--registry-session", sessionId];
|
|
20425
|
-
const child =
|
|
21584
|
+
const child = spawn6(process.execPath, childArgs, {
|
|
20426
21585
|
detached: true,
|
|
20427
21586
|
stdio: "ignore",
|
|
20428
21587
|
cwd: process.cwd(),
|