@nomad-e/bluma-cli 0.1.63 → 0.1.65
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 +1471 -255
- package/package.json +6 -1
package/dist/main.js
CHANGED
|
@@ -1720,10 +1720,10 @@ var init_themes = __esm({
|
|
|
1720
1720
|
import React19 from "react";
|
|
1721
1721
|
import { render } from "ink";
|
|
1722
1722
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
1723
|
-
import
|
|
1724
|
-
import
|
|
1723
|
+
import fs37 from "fs";
|
|
1724
|
+
import path42 from "path";
|
|
1725
1725
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1726
|
-
import { spawn as
|
|
1726
|
+
import { spawn as spawn6 } from "child_process";
|
|
1727
1727
|
import { v4 as uuidv412 } from "uuid";
|
|
1728
1728
|
|
|
1729
1729
|
// src/app/ui/App.tsx
|
|
@@ -2532,7 +2532,7 @@ var filterSlashCommands = (query) => {
|
|
|
2532
2532
|
let tier = 3;
|
|
2533
2533
|
let scorePrimary = 0;
|
|
2534
2534
|
let scoreSecondary = Number.MAX_SAFE_INTEGER;
|
|
2535
|
-
|
|
2535
|
+
const scoreTertiary = name.length;
|
|
2536
2536
|
if (isPrefix) {
|
|
2537
2537
|
tier = 0;
|
|
2538
2538
|
scorePrimary = q.length * -1;
|
|
@@ -4270,12 +4270,12 @@ function EditToolDiffPanel({
|
|
|
4270
4270
|
maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
|
|
4271
4271
|
fallbackSnippet
|
|
4272
4272
|
}) {
|
|
4273
|
-
const
|
|
4273
|
+
const path43 = filePath.trim() || "unknown file";
|
|
4274
4274
|
const diff = diffText?.trim() ?? "";
|
|
4275
4275
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
4276
4276
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexWrap: "wrap", children: /* @__PURE__ */ jsxs5(Text5, { color: isNewFile ? BLUMA_TERMINAL.success : void 0, children: [
|
|
4277
4277
|
isNewFile ? "Created " : "Wrote to ",
|
|
4278
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children:
|
|
4278
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: path43 })
|
|
4279
4279
|
] }) }),
|
|
4280
4280
|
description ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: description }) : null,
|
|
4281
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: [
|
|
@@ -4604,7 +4604,7 @@ var renderFindByName = ({ args }) => {
|
|
|
4604
4604
|
var renderGrepSearch = ({ args }) => {
|
|
4605
4605
|
const parsed = parseArgs(args);
|
|
4606
4606
|
const query = parsed.query || "";
|
|
4607
|
-
const
|
|
4607
|
+
const path43 = parsed.path || ".";
|
|
4608
4608
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
4609
4609
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.muted, children: [
|
|
4610
4610
|
'"',
|
|
@@ -4613,7 +4613,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
4613
4613
|
] }),
|
|
4614
4614
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: [
|
|
4615
4615
|
" ",
|
|
4616
|
-
|
|
4616
|
+
path43
|
|
4617
4617
|
] })
|
|
4618
4618
|
] });
|
|
4619
4619
|
};
|
|
@@ -5039,12 +5039,12 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
5039
5039
|
|
|
5040
5040
|
// src/app/agent/agent.ts
|
|
5041
5041
|
import * as dotenv from "dotenv";
|
|
5042
|
-
import
|
|
5043
|
-
import
|
|
5042
|
+
import path40 from "path";
|
|
5043
|
+
import os27 from "os";
|
|
5044
5044
|
|
|
5045
5045
|
// src/app/agent/tool_invoker.ts
|
|
5046
|
-
import { promises as
|
|
5047
|
-
import
|
|
5046
|
+
import { promises as fs25 } from "fs";
|
|
5047
|
+
import path27 from "path";
|
|
5048
5048
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5049
5049
|
|
|
5050
5050
|
// src/app/agent/tools/natives/edit.ts
|
|
@@ -5099,9 +5099,9 @@ function countOccurrences(text, search) {
|
|
|
5099
5099
|
return text.split(search).length - 1;
|
|
5100
5100
|
}
|
|
5101
5101
|
function ensureCorrectEdit(currentContent, originalOldString, originalNewString, expectedReplacements) {
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5102
|
+
const finalOldString = originalOldString;
|
|
5103
|
+
const finalNewString = originalNewString;
|
|
5104
|
+
const occurrences = countOccurrences(currentContent, finalOldString);
|
|
5105
5105
|
if (occurrences > 0) {
|
|
5106
5106
|
return [finalOldString, finalNewString, occurrences];
|
|
5107
5107
|
}
|
|
@@ -5258,7 +5258,7 @@ async function calculateEdit(filePath, oldString, newString, expectedReplacement
|
|
|
5258
5258
|
};
|
|
5259
5259
|
}
|
|
5260
5260
|
let currentContent = null;
|
|
5261
|
-
|
|
5261
|
+
const isNewFile = false;
|
|
5262
5262
|
try {
|
|
5263
5263
|
const stats = await fs6.stat(normalizedFilePath);
|
|
5264
5264
|
if (stats.size > MAX_FILE_SIZE) {
|
|
@@ -5706,7 +5706,7 @@ async function readLines(args) {
|
|
|
5706
5706
|
if (startIndex >= total_lines) {
|
|
5707
5707
|
throw new Error(`start_line (${startLine}) exceeds file length (${total_lines} lines).`);
|
|
5708
5708
|
}
|
|
5709
|
-
|
|
5709
|
+
const endIndex = Math.min(endLine, total_lines);
|
|
5710
5710
|
const contentLines = lines.slice(startIndex, endIndex);
|
|
5711
5711
|
let content = contentLines.join("\n");
|
|
5712
5712
|
if (args.line_number_prefix === true) {
|
|
@@ -6028,7 +6028,7 @@ var DEFAULT_IGNORE_PATTERNS = [
|
|
|
6028
6028
|
var MAX_RESULTS2 = 100;
|
|
6029
6029
|
var MAX_DEPTH_DEFAULT = 10;
|
|
6030
6030
|
function globToRegex(glob) {
|
|
6031
|
-
|
|
6031
|
+
const regex = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{DOUBLESTAR}}").replace(/\*/g, "[^/]*").replace(/\?/g, ".").replace(/{{DOUBLESTAR}}/g, ".*");
|
|
6032
6032
|
return new RegExp(`^${regex}$`, "i");
|
|
6033
6033
|
}
|
|
6034
6034
|
function shouldIgnore(name, ignorePatterns, includeHidden) {
|
|
@@ -8530,15 +8530,30 @@ async function task_output(params) {
|
|
|
8530
8530
|
|
|
8531
8531
|
// src/app/agent/core/context-api/token_counter.ts
|
|
8532
8532
|
import { getEncoding } from "js-tiktoken";
|
|
8533
|
+
import { createHash } from "crypto";
|
|
8533
8534
|
var MESSAGE_OVERHEAD_TOKENS = 4;
|
|
8534
8535
|
var CONVERSATION_BASE_OVERHEAD = 3;
|
|
8535
8536
|
var cachedEncoding = null;
|
|
8537
|
+
var tokenCountCache = null;
|
|
8536
8538
|
function getO200kEncoding() {
|
|
8537
8539
|
if (!cachedEncoding) {
|
|
8538
8540
|
cachedEncoding = getEncoding("o200k_base");
|
|
8539
8541
|
}
|
|
8540
8542
|
return cachedEncoding;
|
|
8541
8543
|
}
|
|
8544
|
+
function hashHistory(messages) {
|
|
8545
|
+
const hash = createHash("sha256");
|
|
8546
|
+
hash.update(`len:${messages.length}:`);
|
|
8547
|
+
for (const msg of messages) {
|
|
8548
|
+
const m = msg;
|
|
8549
|
+
hash.update(`role:${m.role ?? ""}:`);
|
|
8550
|
+
hash.update(`content:${JSON.stringify(m.content ?? "")}:`);
|
|
8551
|
+
hash.update(`tool_calls:${JSON.stringify(m.tool_calls ?? "")}:`);
|
|
8552
|
+
hash.update(`tool_call_id:${m.tool_call_id ?? ""}:`);
|
|
8553
|
+
hash.update(`name:${m.name ?? ""}:`);
|
|
8554
|
+
}
|
|
8555
|
+
return hash.digest("hex");
|
|
8556
|
+
}
|
|
8542
8557
|
function messageBodyForTokens(msg) {
|
|
8543
8558
|
const c = msg.content;
|
|
8544
8559
|
if (c == null) {
|
|
@@ -8563,10 +8578,16 @@ function messageExtraForTokens(msg) {
|
|
|
8563
8578
|
}
|
|
8564
8579
|
return parts.join("\0");
|
|
8565
8580
|
}
|
|
8566
|
-
function countTokens(messages) {
|
|
8581
|
+
function countTokens(messages, useCache = true) {
|
|
8567
8582
|
if (messages.length === 0) {
|
|
8568
8583
|
return CONVERSATION_BASE_OVERHEAD;
|
|
8569
8584
|
}
|
|
8585
|
+
if (useCache) {
|
|
8586
|
+
const currentHash = hashHistory(messages);
|
|
8587
|
+
if (tokenCountCache && tokenCountCache.hash === currentHash) {
|
|
8588
|
+
return tokenCountCache.count;
|
|
8589
|
+
}
|
|
8590
|
+
}
|
|
8570
8591
|
const enc = getO200kEncoding();
|
|
8571
8592
|
let total = CONVERSATION_BASE_OVERHEAD;
|
|
8572
8593
|
for (const msg of messages) {
|
|
@@ -8576,8 +8597,14 @@ function countTokens(messages) {
|
|
|
8576
8597
|
const nExtra = extra ? enc.encode(extra).length : 0;
|
|
8577
8598
|
total += nBody + nExtra + MESSAGE_OVERHEAD_TOKENS;
|
|
8578
8599
|
}
|
|
8600
|
+
if (useCache) {
|
|
8601
|
+
tokenCountCache = { hash: hashHistory(messages), count: total };
|
|
8602
|
+
}
|
|
8579
8603
|
return total;
|
|
8580
8604
|
}
|
|
8605
|
+
function clearTokenCountCache() {
|
|
8606
|
+
tokenCountCache = null;
|
|
8607
|
+
}
|
|
8581
8608
|
|
|
8582
8609
|
// src/app/agent/tools/natives/ctx_inspect.ts
|
|
8583
8610
|
async function ctx_inspect(args = {}) {
|
|
@@ -8913,7 +8940,7 @@ async function dream(args = {}) {
|
|
|
8913
8940
|
const used = /* @__PURE__ */ new Set();
|
|
8914
8941
|
for (let i = 0; i < entries.length; i++) {
|
|
8915
8942
|
if (used.has(entries[i].id)) continue;
|
|
8916
|
-
|
|
8943
|
+
const current = { ...entries[i] };
|
|
8917
8944
|
used.add(entries[i].id);
|
|
8918
8945
|
for (let j = i + 1; j < entries.length; j++) {
|
|
8919
8946
|
if (used.has(entries[j].id)) continue;
|
|
@@ -9188,6 +9215,1021 @@ async function context_collapse(args = {}) {
|
|
|
9188
9215
|
|
|
9189
9216
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
9190
9217
|
init_agent_coordination();
|
|
9218
|
+
|
|
9219
|
+
// src/app/agent/tools/natives/create-next-app.ts
|
|
9220
|
+
init_sandbox_policy();
|
|
9221
|
+
import { promises as fs23 } from "fs";
|
|
9222
|
+
import path25 from "path";
|
|
9223
|
+
|
|
9224
|
+
// src/app/agent/tools/natives/shell_command.ts
|
|
9225
|
+
init_sandbox_policy();
|
|
9226
|
+
import os15 from "os";
|
|
9227
|
+
import { spawn as spawn5 } from "child_process";
|
|
9228
|
+
var MAX_OUTPUT_SIZE2 = 3e4;
|
|
9229
|
+
var MAX_OUTPUT_LINES = 200;
|
|
9230
|
+
var OUTPUT_TRUNCATION_MESSAGE = "\n\n[OUTPUT TRUNCATED - use `| head -n X` or redirect to file for full output]";
|
|
9231
|
+
var DANGEROUS_PATTERNS = [
|
|
9232
|
+
// === ELEVAÇÃO DE PRIVILÉGIOS ===
|
|
9233
|
+
/^sudo\s+/i,
|
|
9234
|
+
// sudo commands
|
|
9235
|
+
/^doas\s+/i,
|
|
9236
|
+
// doas (BSD sudo alternative)
|
|
9237
|
+
/^su\s+/i,
|
|
9238
|
+
// switch user
|
|
9239
|
+
/^pkexec\s+/i,
|
|
9240
|
+
// PolicyKit execution
|
|
9241
|
+
/\|\s*sudo\s+/i,
|
|
9242
|
+
// piped to sudo
|
|
9243
|
+
/;\s*sudo\s+/i,
|
|
9244
|
+
// chained with sudo
|
|
9245
|
+
/&&\s*sudo\s+/i,
|
|
9246
|
+
// AND chained with sudo
|
|
9247
|
+
// === COMANDOS DESTRUTIVOS ===
|
|
9248
|
+
/\brm\s+(-[rf]+\s+)*[\/~]/i,
|
|
9249
|
+
// rm com paths perigosos (/, ~)
|
|
9250
|
+
/\brm\s+-[rf]*\s+\*/i,
|
|
9251
|
+
// rm -rf *
|
|
9252
|
+
/\bchmod\s+(777|666)\s+\//i,
|
|
9253
|
+
// chmod 777/666 em paths root
|
|
9254
|
+
/\bchown\s+.*\s+\//i,
|
|
9255
|
+
// chown em paths root
|
|
9256
|
+
/\bdd\s+.*of=\/dev\/(sd|hd|nvme)/i,
|
|
9257
|
+
// dd para discos
|
|
9258
|
+
/\bmkfs\./i,
|
|
9259
|
+
// format filesystem
|
|
9260
|
+
/>\s*\/dev\/(sd|hd|nvme)/i,
|
|
9261
|
+
// redirect para disco
|
|
9262
|
+
/\bshred\s+/i,
|
|
9263
|
+
// secure delete
|
|
9264
|
+
// === FORK BOMB / RESOURCE EXHAUSTION ===
|
|
9265
|
+
/:\(\)\s*\{\s*:\|:&\s*\}\s*;:/,
|
|
9266
|
+
// fork bomb clássico
|
|
9267
|
+
/\bwhile\s+true\s*;\s*do/i,
|
|
9268
|
+
// infinite loop
|
|
9269
|
+
/\byes\s+\|/i,
|
|
9270
|
+
// yes piped (resource exhaustion)
|
|
9271
|
+
// === NETWORK PERIGOSO ===
|
|
9272
|
+
/\bcurl\s+.*\|\s*(ba)?sh/i,
|
|
9273
|
+
// curl | bash (remote code exec)
|
|
9274
|
+
/\bwget\s+.*\|\s*(ba)?sh/i,
|
|
9275
|
+
// wget | bash
|
|
9276
|
+
/\bnc\s+-[el]/i
|
|
9277
|
+
// netcat listener (backdoor)
|
|
9278
|
+
];
|
|
9279
|
+
var INTERACTIVE_PATTERNS2 = [
|
|
9280
|
+
/^(vim|vi|nano|emacs|pico)\s*/i,
|
|
9281
|
+
// editors
|
|
9282
|
+
/^(less|more|most)\s*/i,
|
|
9283
|
+
// pagers
|
|
9284
|
+
/^(top|htop|btop|atop|nmon)\s*/i,
|
|
9285
|
+
// monitoring tools
|
|
9286
|
+
/^(ssh|telnet|ftp|sftp)\s+/i,
|
|
9287
|
+
// remote connections
|
|
9288
|
+
/^(mysql|psql|redis-cli|mongo|mongosh)\s*$/i,
|
|
9289
|
+
// database CLIs (sem script)
|
|
9290
|
+
/^(python|python3|node|ruby|irb|lua)\s*$/i,
|
|
9291
|
+
// REPLs sem script
|
|
9292
|
+
/^(gdb|lldb)\s*/i,
|
|
9293
|
+
// debuggers
|
|
9294
|
+
/^(bc|dc)\s*$/i
|
|
9295
|
+
// calculators
|
|
9296
|
+
];
|
|
9297
|
+
function shellCommand(args) {
|
|
9298
|
+
const {
|
|
9299
|
+
command,
|
|
9300
|
+
timeout = 300,
|
|
9301
|
+
// 5 minutos por padrão
|
|
9302
|
+
cwd = process.cwd(),
|
|
9303
|
+
verbose = false
|
|
9304
|
+
} = args;
|
|
9305
|
+
return new Promise((resolve2) => {
|
|
9306
|
+
const startTime = Date.now();
|
|
9307
|
+
const platform = os15.platform();
|
|
9308
|
+
const commandTrimmed = command.trim();
|
|
9309
|
+
let resolvedCwd;
|
|
9310
|
+
try {
|
|
9311
|
+
resolvedCwd = resolveCommandCwd(cwd);
|
|
9312
|
+
} catch (err) {
|
|
9313
|
+
const result = {
|
|
9314
|
+
status: "blocked",
|
|
9315
|
+
exitCode: null,
|
|
9316
|
+
stdout: "",
|
|
9317
|
+
stderr: `Command blocked: ${err.message}`,
|
|
9318
|
+
command,
|
|
9319
|
+
cwd,
|
|
9320
|
+
platform,
|
|
9321
|
+
duration: Date.now() - startTime,
|
|
9322
|
+
blockedReason: "sandbox_path_violation"
|
|
9323
|
+
};
|
|
9324
|
+
return resolve2(formatResult(result, verbose));
|
|
9325
|
+
}
|
|
9326
|
+
for (const pattern of DANGEROUS_PATTERNS) {
|
|
9327
|
+
if (pattern.test(commandTrimmed)) {
|
|
9328
|
+
const result = {
|
|
9329
|
+
status: "blocked",
|
|
9330
|
+
exitCode: null,
|
|
9331
|
+
stdout: "",
|
|
9332
|
+
stderr: `Command blocked: "${commandTrimmed}" requires elevated privileges (sudo/doas). These commands require interactive password input which cannot be handled. Alternatives:
|
|
9333
|
+
1. Run the command manually in terminal
|
|
9334
|
+
2. Configure passwordless sudo for this specific command
|
|
9335
|
+
3. Run BluMa as root (not recommended)`,
|
|
9336
|
+
command,
|
|
9337
|
+
cwd,
|
|
9338
|
+
platform,
|
|
9339
|
+
duration: Date.now() - startTime,
|
|
9340
|
+
blockedReason: "requires_sudo"
|
|
9341
|
+
};
|
|
9342
|
+
return resolve2(formatResult(result, verbose));
|
|
9343
|
+
}
|
|
9344
|
+
}
|
|
9345
|
+
for (const pattern of INTERACTIVE_PATTERNS2) {
|
|
9346
|
+
if (pattern.test(commandTrimmed)) {
|
|
9347
|
+
const result = {
|
|
9348
|
+
status: "blocked",
|
|
9349
|
+
exitCode: null,
|
|
9350
|
+
stdout: "",
|
|
9351
|
+
stderr: `Command blocked: "${commandTrimmed}" is an interactive command. Interactive commands require user input and cannot be handled by the agent. Alternatives:
|
|
9352
|
+
1. Use non-interactive alternatives (e.g., 'cat' instead of 'less')
|
|
9353
|
+
2. Run the command manually in terminal
|
|
9354
|
+
3. For editors, use edit_tool instead`,
|
|
9355
|
+
command,
|
|
9356
|
+
cwd,
|
|
9357
|
+
platform,
|
|
9358
|
+
duration: Date.now() - startTime,
|
|
9359
|
+
blockedReason: "interactive_command"
|
|
9360
|
+
};
|
|
9361
|
+
return resolve2(formatResult(result, verbose));
|
|
9362
|
+
}
|
|
9363
|
+
}
|
|
9364
|
+
let shellCmd;
|
|
9365
|
+
let shellArgs;
|
|
9366
|
+
if (platform === "win32") {
|
|
9367
|
+
shellCmd = process.env.COMSPEC || "cmd.exe";
|
|
9368
|
+
shellArgs = ["/c", command];
|
|
9369
|
+
} else {
|
|
9370
|
+
shellCmd = process.env.SHELL || "/bin/bash";
|
|
9371
|
+
shellArgs = ["-c", command];
|
|
9372
|
+
}
|
|
9373
|
+
let stdout = "";
|
|
9374
|
+
let stderr = "";
|
|
9375
|
+
let stdoutTruncated = false;
|
|
9376
|
+
let stderrTruncated = false;
|
|
9377
|
+
let timedOut = false;
|
|
9378
|
+
let finished = false;
|
|
9379
|
+
const childProcess = spawn5(shellCmd, shellArgs, {
|
|
9380
|
+
cwd: resolvedCwd,
|
|
9381
|
+
env: process.env,
|
|
9382
|
+
// Importante: no Windows, precisamos do shell, mas spawn já lida com isso
|
|
9383
|
+
windowsHide: true
|
|
9384
|
+
});
|
|
9385
|
+
const timeoutId = setTimeout(() => {
|
|
9386
|
+
if (!finished) {
|
|
9387
|
+
timedOut = true;
|
|
9388
|
+
childProcess.kill("SIGTERM");
|
|
9389
|
+
setTimeout(() => {
|
|
9390
|
+
if (!finished) {
|
|
9391
|
+
childProcess.kill("SIGKILL");
|
|
9392
|
+
}
|
|
9393
|
+
}, 2e3);
|
|
9394
|
+
}
|
|
9395
|
+
}, timeout * 1e3);
|
|
9396
|
+
if (childProcess.stdout) {
|
|
9397
|
+
childProcess.stdout.on("data", (data) => {
|
|
9398
|
+
if (!stdoutTruncated) {
|
|
9399
|
+
stdout += data.toString();
|
|
9400
|
+
if (stdout.length > MAX_OUTPUT_SIZE2) {
|
|
9401
|
+
stdout = stdout.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MESSAGE;
|
|
9402
|
+
stdoutTruncated = true;
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
});
|
|
9406
|
+
}
|
|
9407
|
+
if (childProcess.stderr) {
|
|
9408
|
+
childProcess.stderr.on("data", (data) => {
|
|
9409
|
+
if (!stderrTruncated) {
|
|
9410
|
+
stderr += data.toString();
|
|
9411
|
+
if (stderr.length > MAX_OUTPUT_SIZE2) {
|
|
9412
|
+
stderr = stderr.substring(0, MAX_OUTPUT_SIZE2) + OUTPUT_TRUNCATION_MESSAGE;
|
|
9413
|
+
stderrTruncated = true;
|
|
9414
|
+
}
|
|
9415
|
+
}
|
|
9416
|
+
});
|
|
9417
|
+
}
|
|
9418
|
+
childProcess.on("error", (error) => {
|
|
9419
|
+
if (!finished) {
|
|
9420
|
+
finished = true;
|
|
9421
|
+
clearTimeout(timeoutId);
|
|
9422
|
+
const result = {
|
|
9423
|
+
status: "error",
|
|
9424
|
+
exitCode: null,
|
|
9425
|
+
stdout: stdout.trim(),
|
|
9426
|
+
stderr: `Failed to execute command: ${error.message}`,
|
|
9427
|
+
command,
|
|
9428
|
+
cwd,
|
|
9429
|
+
platform,
|
|
9430
|
+
duration: Date.now() - startTime
|
|
9431
|
+
};
|
|
9432
|
+
resolve2(formatResult(result, verbose));
|
|
9433
|
+
}
|
|
9434
|
+
});
|
|
9435
|
+
childProcess.on("close", (code, signal) => {
|
|
9436
|
+
if (!finished) {
|
|
9437
|
+
finished = true;
|
|
9438
|
+
clearTimeout(timeoutId);
|
|
9439
|
+
const result = {
|
|
9440
|
+
status: timedOut ? "timeout" : code === 0 ? "success" : "error",
|
|
9441
|
+
exitCode: code,
|
|
9442
|
+
stdout: stdout.trim(),
|
|
9443
|
+
stderr: timedOut ? `Command timed out after ${timeout} seconds
|
|
9444
|
+
${stderr.trim()}` : stderr.trim(),
|
|
9445
|
+
command,
|
|
9446
|
+
cwd,
|
|
9447
|
+
platform,
|
|
9448
|
+
duration: Date.now() - startTime,
|
|
9449
|
+
truncated: stdoutTruncated || stderrTruncated
|
|
9450
|
+
};
|
|
9451
|
+
resolve2(formatResult(result, verbose));
|
|
9452
|
+
}
|
|
9453
|
+
});
|
|
9454
|
+
});
|
|
9455
|
+
}
|
|
9456
|
+
function smartTruncate(text) {
|
|
9457
|
+
if (!text) return { result: "", wasTruncated: false };
|
|
9458
|
+
let result = text;
|
|
9459
|
+
let wasTruncated = false;
|
|
9460
|
+
if (result.length > MAX_OUTPUT_SIZE2) {
|
|
9461
|
+
result = result.substring(0, MAX_OUTPUT_SIZE2);
|
|
9462
|
+
wasTruncated = true;
|
|
9463
|
+
}
|
|
9464
|
+
const lines = result.split("\n");
|
|
9465
|
+
if (lines.length > MAX_OUTPUT_LINES) {
|
|
9466
|
+
const head = lines.slice(0, 50);
|
|
9467
|
+
const tail = lines.slice(-50);
|
|
9468
|
+
result = head.join("\n") + `
|
|
9469
|
+
|
|
9470
|
+
... [${lines.length - 100} lines omitted] ...
|
|
9471
|
+
|
|
9472
|
+
` + tail.join("\n");
|
|
9473
|
+
wasTruncated = true;
|
|
9474
|
+
}
|
|
9475
|
+
return { result, wasTruncated };
|
|
9476
|
+
}
|
|
9477
|
+
function formatResult(result, verbose) {
|
|
9478
|
+
const stdout = smartTruncate(result.stdout);
|
|
9479
|
+
const stderr = smartTruncate(result.stderr);
|
|
9480
|
+
const wasTruncated = result.truncated || stdout.wasTruncated || stderr.wasTruncated;
|
|
9481
|
+
if (verbose) {
|
|
9482
|
+
return JSON.stringify({
|
|
9483
|
+
...result,
|
|
9484
|
+
stdout: stdout.result,
|
|
9485
|
+
stderr: stderr.result,
|
|
9486
|
+
truncated: wasTruncated
|
|
9487
|
+
}, null, 2);
|
|
9488
|
+
}
|
|
9489
|
+
const output = {
|
|
9490
|
+
status: result.status,
|
|
9491
|
+
exitCode: result.exitCode
|
|
9492
|
+
};
|
|
9493
|
+
if (stdout.result) {
|
|
9494
|
+
output.stdout = stdout.result;
|
|
9495
|
+
}
|
|
9496
|
+
if (stderr.result) {
|
|
9497
|
+
output.stderr = stderr.result;
|
|
9498
|
+
}
|
|
9499
|
+
if (result.status === "timeout") {
|
|
9500
|
+
output.message = `Command exceeded timeout of ${result.duration / 1e3}s`;
|
|
9501
|
+
}
|
|
9502
|
+
if (result.status === "blocked" && result.blockedReason) {
|
|
9503
|
+
output.blockedReason = result.blockedReason;
|
|
9504
|
+
}
|
|
9505
|
+
if (wasTruncated) {
|
|
9506
|
+
output.warning = "Output truncated (30KB / 200 lines max). Use `| head -n X` or redirect to file.";
|
|
9507
|
+
}
|
|
9508
|
+
return JSON.stringify(output, null, 2);
|
|
9509
|
+
}
|
|
9510
|
+
|
|
9511
|
+
// src/app/agent/tools/natives/create-next-app.ts
|
|
9512
|
+
var MINIMAL_FILES = {
|
|
9513
|
+
"package.json": `{
|
|
9514
|
+
"name": "{{NAME}}",
|
|
9515
|
+
"version": "0.1.0",
|
|
9516
|
+
"private": true,
|
|
9517
|
+
"scripts": {
|
|
9518
|
+
"dev": "next dev",
|
|
9519
|
+
"build": "next build",
|
|
9520
|
+
"start": "next start",
|
|
9521
|
+
"lint": "next lint"
|
|
9522
|
+
},
|
|
9523
|
+
"dependencies": {
|
|
9524
|
+
"next": "^14.1.0",
|
|
9525
|
+
"react": "^18.2.0",
|
|
9526
|
+
"react-dom": "^18.2.0"
|
|
9527
|
+
},
|
|
9528
|
+
"devDependencies": {
|
|
9529
|
+
"@types/node": "^20.11.0",
|
|
9530
|
+
"@types/react": "^18.2.0",
|
|
9531
|
+
"@types/react-dom": "^18.2.0",
|
|
9532
|
+
"typescript": "^5.3.0",
|
|
9533
|
+
"tailwindcss": "^3.4.0",
|
|
9534
|
+
"postcss": "^8.4.0",
|
|
9535
|
+
"autoprefixer": "^10.4.0",
|
|
9536
|
+
"eslint": "^8.56.0",
|
|
9537
|
+
"eslint-config-next": "^14.1.0"
|
|
9538
|
+
}
|
|
9539
|
+
}
|
|
9540
|
+
`,
|
|
9541
|
+
"tsconfig.json": `{
|
|
9542
|
+
"compilerOptions": {
|
|
9543
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
9544
|
+
"allowJs": true,
|
|
9545
|
+
"skipLibCheck": true,
|
|
9546
|
+
"strict": true,
|
|
9547
|
+
"noEmit": true,
|
|
9548
|
+
"esModuleInterop": true,
|
|
9549
|
+
"module": "esnext",
|
|
9550
|
+
"moduleResolution": "bundler",
|
|
9551
|
+
"resolveJsonModule": true,
|
|
9552
|
+
"isolatedModules": true,
|
|
9553
|
+
"jsx": "preserve",
|
|
9554
|
+
"incremental": true,
|
|
9555
|
+
"plugins": [
|
|
9556
|
+
{
|
|
9557
|
+
"name": "next"
|
|
9558
|
+
}
|
|
9559
|
+
],
|
|
9560
|
+
"paths": {
|
|
9561
|
+
"@/*": ["./*"]
|
|
9562
|
+
}
|
|
9563
|
+
},
|
|
9564
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
|
9565
|
+
"exclude": ["node_modules"]
|
|
9566
|
+
}
|
|
9567
|
+
`,
|
|
9568
|
+
"next.config.js": `/** @type {import('next').NextConfig} */
|
|
9569
|
+
const nextConfig = {
|
|
9570
|
+
output: 'standalone',
|
|
9571
|
+
reactStrictMode: true,
|
|
9572
|
+
}
|
|
9573
|
+
|
|
9574
|
+
module.exports = nextConfig
|
|
9575
|
+
`,
|
|
9576
|
+
"tailwind.config.ts": `import type { Config } from 'tailwindcss'
|
|
9577
|
+
|
|
9578
|
+
const config: Config = {
|
|
9579
|
+
content: [
|
|
9580
|
+
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9581
|
+
'./components/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9582
|
+
'./app/**/*.{js,ts,jsx,tsx,mdx}',
|
|
9583
|
+
],
|
|
9584
|
+
theme: {
|
|
9585
|
+
extend: {
|
|
9586
|
+
backgroundImage: {
|
|
9587
|
+
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
|
9588
|
+
'gradient-conic':
|
|
9589
|
+
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
|
|
9590
|
+
},
|
|
9591
|
+
},
|
|
9592
|
+
},
|
|
9593
|
+
plugins: [],
|
|
9594
|
+
}
|
|
9595
|
+
export default config
|
|
9596
|
+
`,
|
|
9597
|
+
"postcss.config.js": `module.exports = {
|
|
9598
|
+
plugins: {
|
|
9599
|
+
tailwindcss: {},
|
|
9600
|
+
autoprefixer: {},
|
|
9601
|
+
},
|
|
9602
|
+
}
|
|
9603
|
+
`,
|
|
9604
|
+
".eslintrc.json": `{
|
|
9605
|
+
"extends": "next/core-web-vitals"
|
|
9606
|
+
}
|
|
9607
|
+
`,
|
|
9608
|
+
".gitignore": `# dependencies
|
|
9609
|
+
/node_modules
|
|
9610
|
+
/.pnp
|
|
9611
|
+
.pnp.js
|
|
9612
|
+
|
|
9613
|
+
# testing
|
|
9614
|
+
/coverage
|
|
9615
|
+
|
|
9616
|
+
# next.js
|
|
9617
|
+
/.next/
|
|
9618
|
+
/out/
|
|
9619
|
+
|
|
9620
|
+
# production
|
|
9621
|
+
/build
|
|
9622
|
+
|
|
9623
|
+
# misc
|
|
9624
|
+
.DS_Store
|
|
9625
|
+
*.pem
|
|
9626
|
+
|
|
9627
|
+
# debug
|
|
9628
|
+
npm-debug.log*
|
|
9629
|
+
yarn-debug.log*
|
|
9630
|
+
yarn-error.log*
|
|
9631
|
+
|
|
9632
|
+
# local env files
|
|
9633
|
+
.env*.local
|
|
9634
|
+
.env
|
|
9635
|
+
|
|
9636
|
+
# typescript
|
|
9637
|
+
*.tsbuildinfo
|
|
9638
|
+
next-env.d.ts
|
|
9639
|
+
`
|
|
9640
|
+
};
|
|
9641
|
+
var FULL_FILES = {
|
|
9642
|
+
...MINIMAL_FILES,
|
|
9643
|
+
// App Router structure
|
|
9644
|
+
"app/layout.tsx": `import type { Metadata } from "next";
|
|
9645
|
+
import { Inter } from "next/font/google";
|
|
9646
|
+
import "./globals.css";
|
|
9647
|
+
|
|
9648
|
+
const inter = Inter({ subsets: ["latin"] });
|
|
9649
|
+
|
|
9650
|
+
export const metadata: Metadata = {
|
|
9651
|
+
title: "{{NAME}}",
|
|
9652
|
+
description: "Generated by create-next-app",
|
|
9653
|
+
};
|
|
9654
|
+
|
|
9655
|
+
export default function RootLayout({
|
|
9656
|
+
children,
|
|
9657
|
+
}: Readonly<{
|
|
9658
|
+
children: React.ReactNode;
|
|
9659
|
+
}>) {
|
|
9660
|
+
return (
|
|
9661
|
+
<html lang="en">
|
|
9662
|
+
<body className={inter.className}>{children}</body>
|
|
9663
|
+
</html>
|
|
9664
|
+
);
|
|
9665
|
+
}
|
|
9666
|
+
`,
|
|
9667
|
+
"app/page.tsx": `export default function Home() {
|
|
9668
|
+
return (
|
|
9669
|
+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
|
9670
|
+
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
|
9671
|
+
<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">
|
|
9672
|
+
Welcome to <code className="font-mono font-bold">{{NAME}}</code>
|
|
9673
|
+
</p>
|
|
9674
|
+
</div>
|
|
9675
|
+
|
|
9676
|
+
<div className="mb-32 grid text-center lg:max-w-5xl lg:w-full lg:mb-0 lg:grid-cols-4 lg:text-left">
|
|
9677
|
+
<a
|
|
9678
|
+
href="https://nextjs.org/docs"
|
|
9679
|
+
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"
|
|
9680
|
+
target="_blank"
|
|
9681
|
+
rel="noopener noreferrer"
|
|
9682
|
+
>
|
|
9683
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9684
|
+
Docs{" "}
|
|
9685
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9686
|
+
->
|
|
9687
|
+
</span>
|
|
9688
|
+
</h2>
|
|
9689
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9690
|
+
Find in-depth information about Next.js features and API.
|
|
9691
|
+
</p>
|
|
9692
|
+
</a>
|
|
9693
|
+
|
|
9694
|
+
<a
|
|
9695
|
+
href="https://nextjs.org/learn"
|
|
9696
|
+
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"
|
|
9697
|
+
target="_blank"
|
|
9698
|
+
rel="noopener noreferrer"
|
|
9699
|
+
>
|
|
9700
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9701
|
+
Learn{" "}
|
|
9702
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9703
|
+
->
|
|
9704
|
+
</span>
|
|
9705
|
+
</h2>
|
|
9706
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9707
|
+
Learn about Next.js in an interactive course with quizzes!
|
|
9708
|
+
</p>
|
|
9709
|
+
</a>
|
|
9710
|
+
|
|
9711
|
+
<a
|
|
9712
|
+
href="https://vercel.com/templates"
|
|
9713
|
+
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"
|
|
9714
|
+
target="_blank"
|
|
9715
|
+
rel="noopener noreferrer"
|
|
9716
|
+
>
|
|
9717
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9718
|
+
Templates{" "}
|
|
9719
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9720
|
+
->
|
|
9721
|
+
</span>
|
|
9722
|
+
</h2>
|
|
9723
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50\`}>
|
|
9724
|
+
Explore starter templates for Next.js.
|
|
9725
|
+
</p>
|
|
9726
|
+
</a>
|
|
9727
|
+
|
|
9728
|
+
<a
|
|
9729
|
+
href="https://vercel.com/new"
|
|
9730
|
+
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"
|
|
9731
|
+
target="_blank"
|
|
9732
|
+
rel="noopener noreferrer"
|
|
9733
|
+
>
|
|
9734
|
+
<h2 className={\`mb-3 text-2xl font-semibold\`}>
|
|
9735
|
+
Deploy{" "}
|
|
9736
|
+
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
|
9737
|
+
->
|
|
9738
|
+
</span>
|
|
9739
|
+
</h2>
|
|
9740
|
+
<p className={\`m-0 max-w-[30ch] text-sm opacity-50 text-balance\`}>
|
|
9741
|
+
Instantly deploy your Next.js site to a shareable URL with Vercel.
|
|
9742
|
+
</p>
|
|
9743
|
+
</a>
|
|
9744
|
+
</div>
|
|
9745
|
+
</main>
|
|
9746
|
+
);
|
|
9747
|
+
}
|
|
9748
|
+
`,
|
|
9749
|
+
"app/globals.css": `@tailwind base;
|
|
9750
|
+
@tailwind components;
|
|
9751
|
+
@tailwind utilities;
|
|
9752
|
+
|
|
9753
|
+
:root {
|
|
9754
|
+
--foreground-rgb: 0, 0, 0;
|
|
9755
|
+
--background-start-rgb: 214, 219, 220;
|
|
9756
|
+
--background-end-rgb: 255, 255, 255;
|
|
9757
|
+
}
|
|
9758
|
+
|
|
9759
|
+
@media (prefers-color-scheme: dark) {
|
|
9760
|
+
:root {
|
|
9761
|
+
--foreground-rgb: 255, 255, 255;
|
|
9762
|
+
--background-start-rgb: 0, 0, 0;
|
|
9763
|
+
--background-end-rgb: 0, 0, 0;
|
|
9764
|
+
}
|
|
9765
|
+
}
|
|
9766
|
+
|
|
9767
|
+
body {
|
|
9768
|
+
color: rgb(var(--foreground-rgb));
|
|
9769
|
+
background: linear-gradient(
|
|
9770
|
+
to bottom,
|
|
9771
|
+
transparent,
|
|
9772
|
+
rgb(var(--background-end-rgb))
|
|
9773
|
+
)
|
|
9774
|
+
rgb(var(--background-start-rgb));
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
@layer utilities {
|
|
9778
|
+
.text-balance {
|
|
9779
|
+
text-wrap: balance;
|
|
9780
|
+
}
|
|
9781
|
+
}
|
|
9782
|
+
`,
|
|
9783
|
+
// shadcn/ui base components
|
|
9784
|
+
"components/ui/button.tsx": `import * as React from "react"
|
|
9785
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
9786
|
+
import { cn } from "@/lib/utils"
|
|
9787
|
+
|
|
9788
|
+
const buttonVariants = cva(
|
|
9789
|
+
"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",
|
|
9790
|
+
{
|
|
9791
|
+
variants: {
|
|
9792
|
+
variant: {
|
|
9793
|
+
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
9794
|
+
destructive:
|
|
9795
|
+
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
|
9796
|
+
outline:
|
|
9797
|
+
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
|
9798
|
+
secondary:
|
|
9799
|
+
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
9800
|
+
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
9801
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
9802
|
+
},
|
|
9803
|
+
size: {
|
|
9804
|
+
default: "h-10 px-4 py-2",
|
|
9805
|
+
sm: "h-9 rounded-md px-3",
|
|
9806
|
+
lg: "h-11 rounded-md px-8",
|
|
9807
|
+
icon: "h-10 w-10",
|
|
9808
|
+
},
|
|
9809
|
+
},
|
|
9810
|
+
defaultVariants: {
|
|
9811
|
+
variant: "default",
|
|
9812
|
+
size: "default",
|
|
9813
|
+
},
|
|
9814
|
+
}
|
|
9815
|
+
)
|
|
9816
|
+
|
|
9817
|
+
export interface ButtonProps
|
|
9818
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
9819
|
+
VariantProps<typeof buttonVariants> {
|
|
9820
|
+
asChild?: boolean
|
|
9821
|
+
}
|
|
9822
|
+
|
|
9823
|
+
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
9824
|
+
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
9825
|
+
const Comp = asChild ? Slot : "button"
|
|
9826
|
+
return (
|
|
9827
|
+
<Comp
|
|
9828
|
+
className={cn(buttonVariants({ variant, size, className }))}
|
|
9829
|
+
ref={ref}
|
|
9830
|
+
{...props}
|
|
9831
|
+
/>
|
|
9832
|
+
)
|
|
9833
|
+
}
|
|
9834
|
+
)
|
|
9835
|
+
Button.displayName = "Button"
|
|
9836
|
+
|
|
9837
|
+
export { Button, buttonVariants }
|
|
9838
|
+
`,
|
|
9839
|
+
"components/ui/card.tsx": `import * as React from "react"
|
|
9840
|
+
import { cn } from "@/lib/utils"
|
|
9841
|
+
|
|
9842
|
+
const Card = React.forwardRef<
|
|
9843
|
+
HTMLDivElement,
|
|
9844
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9845
|
+
>(({ className, ...props }, ref) => (
|
|
9846
|
+
<div
|
|
9847
|
+
ref={ref}
|
|
9848
|
+
className={cn(
|
|
9849
|
+
"rounded-lg border bg-card text-card-foreground shadow-sm",
|
|
9850
|
+
className
|
|
9851
|
+
)}
|
|
9852
|
+
{...props}
|
|
9853
|
+
/>
|
|
9854
|
+
))
|
|
9855
|
+
Card.displayName = "Card"
|
|
9856
|
+
|
|
9857
|
+
const CardHeader = React.forwardRef<
|
|
9858
|
+
HTMLDivElement,
|
|
9859
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9860
|
+
>(({ className, ...props }, ref) => (
|
|
9861
|
+
<div
|
|
9862
|
+
ref={ref}
|
|
9863
|
+
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
|
9864
|
+
{...props}
|
|
9865
|
+
/>
|
|
9866
|
+
))
|
|
9867
|
+
CardHeader.displayName = "CardHeader"
|
|
9868
|
+
|
|
9869
|
+
const CardTitle = React.forwardRef<
|
|
9870
|
+
HTMLParagraphElement,
|
|
9871
|
+
React.HTMLAttributes<HTMLHeadingElement>
|
|
9872
|
+
>(({ className, ...props }, ref) => (
|
|
9873
|
+
<h3
|
|
9874
|
+
ref={ref}
|
|
9875
|
+
className={cn(
|
|
9876
|
+
"text-2xl font-semibold leading-none tracking-tight",
|
|
9877
|
+
className
|
|
9878
|
+
)}
|
|
9879
|
+
{...props}
|
|
9880
|
+
/>
|
|
9881
|
+
))
|
|
9882
|
+
CardTitle.displayName = "CardTitle"
|
|
9883
|
+
|
|
9884
|
+
const CardDescription = React.forwardRef<
|
|
9885
|
+
HTMLParagraphElement,
|
|
9886
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
9887
|
+
>(({ className, ...props }, ref) => (
|
|
9888
|
+
<p
|
|
9889
|
+
ref={ref}
|
|
9890
|
+
className={cn("text-sm text-muted-foreground", className)}
|
|
9891
|
+
{...props}
|
|
9892
|
+
/>
|
|
9893
|
+
))
|
|
9894
|
+
CardDescription.displayName = "CardDescription"
|
|
9895
|
+
|
|
9896
|
+
const CardContent = React.forwardRef<
|
|
9897
|
+
HTMLDivElement,
|
|
9898
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9899
|
+
>(({ className, ...props }, ref) => (
|
|
9900
|
+
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
|
9901
|
+
))
|
|
9902
|
+
CardContent.displayName = "CardContent"
|
|
9903
|
+
|
|
9904
|
+
const CardFooter = React.forwardRef<
|
|
9905
|
+
HTMLDivElement,
|
|
9906
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
9907
|
+
>(({ className, ...props }, ref) => (
|
|
9908
|
+
<div
|
|
9909
|
+
ref={ref}
|
|
9910
|
+
className={cn("flex items-center p-6 pt-0", className)}
|
|
9911
|
+
{...props}
|
|
9912
|
+
/>
|
|
9913
|
+
))
|
|
9914
|
+
CardFooter.displayName = "CardFooter"
|
|
9915
|
+
|
|
9916
|
+
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
|
|
9917
|
+
`,
|
|
9918
|
+
"components/ui/input.tsx": `import * as React from "react"
|
|
9919
|
+
import { cn } from "@/lib/utils"
|
|
9920
|
+
|
|
9921
|
+
export interface InputProps
|
|
9922
|
+
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
9923
|
+
|
|
9924
|
+
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
9925
|
+
({ className, type, ...props }, ref) => {
|
|
9926
|
+
return (
|
|
9927
|
+
<input
|
|
9928
|
+
type={type}
|
|
9929
|
+
className={cn(
|
|
9930
|
+
"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",
|
|
9931
|
+
className
|
|
9932
|
+
)}
|
|
9933
|
+
ref={ref}
|
|
9934
|
+
{...props}
|
|
9935
|
+
/>
|
|
9936
|
+
)
|
|
9937
|
+
}
|
|
9938
|
+
)
|
|
9939
|
+
Input.displayName = "Input"
|
|
9940
|
+
|
|
9941
|
+
export { Input }
|
|
9942
|
+
`,
|
|
9943
|
+
"lib/utils.ts": `import { type ClassValue, clsx } from "clsx"
|
|
9944
|
+
import { twMerge } from "tailwind-merge"
|
|
9945
|
+
|
|
9946
|
+
export function cn(...inputs: ClassValue[]) {
|
|
9947
|
+
return twMerge(clsx(inputs))
|
|
9948
|
+
}
|
|
9949
|
+
`,
|
|
9950
|
+
// Add clsx and tailwind-merge to package.json dependencies
|
|
9951
|
+
"package.json.full": `{
|
|
9952
|
+
"name": "{{NAME}}",
|
|
9953
|
+
"version": "0.1.0",
|
|
9954
|
+
"private": true,
|
|
9955
|
+
"scripts": {
|
|
9956
|
+
"dev": "next dev",
|
|
9957
|
+
"build": "next build",
|
|
9958
|
+
"start": "next start",
|
|
9959
|
+
"lint": "next lint"
|
|
9960
|
+
},
|
|
9961
|
+
"dependencies": {
|
|
9962
|
+
"next": "^14.1.0",
|
|
9963
|
+
"react": "^18.2.0",
|
|
9964
|
+
"react-dom": "^18.2.0",
|
|
9965
|
+
"class-variance-authority": "^0.7.0",
|
|
9966
|
+
"clsx": "^2.1.0",
|
|
9967
|
+
"tailwind-merge": "^2.2.0"
|
|
9968
|
+
},
|
|
9969
|
+
"devDependencies": {
|
|
9970
|
+
"@types/node": "^20.11.0",
|
|
9971
|
+
"@types/react": "^18.2.0",
|
|
9972
|
+
"@types/react-dom": "^18.2.0",
|
|
9973
|
+
"typescript": "^5.3.0",
|
|
9974
|
+
"tailwindcss": "^3.4.0",
|
|
9975
|
+
"postcss": "^8.4.0",
|
|
9976
|
+
"autoprefixer": "^10.4.0",
|
|
9977
|
+
"eslint": "^8.56.0",
|
|
9978
|
+
"eslint-config-next": "^14.1.0"
|
|
9979
|
+
}
|
|
9980
|
+
}
|
|
9981
|
+
`
|
|
9982
|
+
};
|
|
9983
|
+
async function createFile(filePath, content, projectName) {
|
|
9984
|
+
const dir = path25.dirname(filePath);
|
|
9985
|
+
await fs23.mkdir(dir, { recursive: true });
|
|
9986
|
+
const replacedContent = content.replace(/{{NAME}}/g, projectName);
|
|
9987
|
+
await fs23.writeFile(filePath, replacedContent, "utf-8");
|
|
9988
|
+
}
|
|
9989
|
+
async function createNextApp(args) {
|
|
9990
|
+
const {
|
|
9991
|
+
name,
|
|
9992
|
+
directory,
|
|
9993
|
+
template = "full"
|
|
9994
|
+
} = args;
|
|
9995
|
+
try {
|
|
9996
|
+
if (!name || typeof name !== "string") {
|
|
9997
|
+
return { success: false, error: "name is required and must be a string" };
|
|
9998
|
+
}
|
|
9999
|
+
if (!/^[a-z0-9-_]+$/.test(name)) {
|
|
10000
|
+
return {
|
|
10001
|
+
success: false,
|
|
10002
|
+
error: "name must contain only lowercase letters, numbers, hyphens, and underscores"
|
|
10003
|
+
};
|
|
10004
|
+
}
|
|
10005
|
+
const baseDir = directory ? resolveWorkspacePath(directory) : resolveWorkspacePath(".");
|
|
10006
|
+
const projectPath = path25.join(baseDir, name);
|
|
10007
|
+
try {
|
|
10008
|
+
await fs23.access(projectPath);
|
|
10009
|
+
return {
|
|
10010
|
+
success: false,
|
|
10011
|
+
error: `Directory already exists: ${projectPath}. Remove it first or choose a different name.`
|
|
10012
|
+
};
|
|
10013
|
+
} catch {
|
|
10014
|
+
}
|
|
10015
|
+
await fs23.mkdir(projectPath, { recursive: true });
|
|
10016
|
+
const filesToCreate = template === "minimal" ? MINIMAL_FILES : FULL_FILES;
|
|
10017
|
+
const filesCreated = [];
|
|
10018
|
+
for (const [relativePath, content] of Object.entries(filesToCreate)) {
|
|
10019
|
+
if (relativePath === "package.json.full") continue;
|
|
10020
|
+
const fullPath = path25.join(projectPath, relativePath);
|
|
10021
|
+
await createFile(fullPath, content, name);
|
|
10022
|
+
filesCreated.push(relativePath);
|
|
10023
|
+
}
|
|
10024
|
+
if (template === "full") {
|
|
10025
|
+
const packageJsonPath = path25.join(projectPath, "package.json");
|
|
10026
|
+
const fullPackageJson = FULL_FILES["package.json.full"];
|
|
10027
|
+
if (fullPackageJson) {
|
|
10028
|
+
await createFile(packageJsonPath, fullPackageJson, name);
|
|
10029
|
+
}
|
|
10030
|
+
}
|
|
10031
|
+
console.log(`[create-next-app] Installing dependencies in ${projectPath}...`);
|
|
10032
|
+
const installResult = await shellCommand({
|
|
10033
|
+
command: "npm install",
|
|
10034
|
+
cwd: projectPath,
|
|
10035
|
+
timeout: 300
|
|
10036
|
+
// 5 minutos
|
|
10037
|
+
});
|
|
10038
|
+
const installJson = JSON.parse(installResult);
|
|
10039
|
+
if (installJson.status !== "success") {
|
|
10040
|
+
console.warn("[create-next-app] npm install warnings:", installJson.stderr);
|
|
10041
|
+
}
|
|
10042
|
+
const nextSteps = [
|
|
10043
|
+
`cd ${name}`,
|
|
10044
|
+
"npm run dev",
|
|
10045
|
+
"Open http://localhost:3000",
|
|
10046
|
+
template === "full" ? "Components shadcn/ui dispon\xEDveis em components/ui/" : "Adicione componentes conforme necess\xE1rio"
|
|
10047
|
+
];
|
|
10048
|
+
console.log(`[create-next-app] Projeto criado: ${projectPath}`);
|
|
10049
|
+
return {
|
|
10050
|
+
success: true,
|
|
10051
|
+
projectPath,
|
|
10052
|
+
directory: name,
|
|
10053
|
+
filesCreated,
|
|
10054
|
+
nextSteps
|
|
10055
|
+
};
|
|
10056
|
+
} catch (error) {
|
|
10057
|
+
console.error("[create-next-app] Error:", error.message);
|
|
10058
|
+
return {
|
|
10059
|
+
success: false,
|
|
10060
|
+
error: error.message || String(error)
|
|
10061
|
+
};
|
|
10062
|
+
}
|
|
10063
|
+
}
|
|
10064
|
+
|
|
10065
|
+
// src/app/agent/tools/natives/deploy-app.ts
|
|
10066
|
+
init_sandbox_policy();
|
|
10067
|
+
import { promises as fs24 } from "fs";
|
|
10068
|
+
import path26 from "path";
|
|
10069
|
+
var EXCLUDE_PATTERNS = [
|
|
10070
|
+
"node_modules",
|
|
10071
|
+
".next",
|
|
10072
|
+
".git",
|
|
10073
|
+
".gitignore",
|
|
10074
|
+
"dist",
|
|
10075
|
+
"build",
|
|
10076
|
+
"*.log",
|
|
10077
|
+
".DS_Store",
|
|
10078
|
+
".env.local",
|
|
10079
|
+
".env*.local",
|
|
10080
|
+
"coverage",
|
|
10081
|
+
".vercel",
|
|
10082
|
+
".turbo"
|
|
10083
|
+
];
|
|
10084
|
+
async function createProjectZip(projectDir, zipPath) {
|
|
10085
|
+
const excludes = [];
|
|
10086
|
+
for (const pattern of EXCLUDE_PATTERNS) {
|
|
10087
|
+
excludes.push("--exclude", pattern);
|
|
10088
|
+
}
|
|
10089
|
+
const zipCommand = `zip -r "${zipPath}" . ${excludes.join(" ")}`;
|
|
10090
|
+
const result = await shellCommand({
|
|
10091
|
+
command: zipCommand,
|
|
10092
|
+
cwd: projectDir,
|
|
10093
|
+
timeout: 120
|
|
10094
|
+
// 2 minutos para zippar
|
|
10095
|
+
});
|
|
10096
|
+
const resultJson = JSON.parse(result);
|
|
10097
|
+
if (resultJson.status !== "success") {
|
|
10098
|
+
throw new Error(`Failed to create ZIP: ${resultJson.stderr}`);
|
|
10099
|
+
}
|
|
10100
|
+
return zipPath;
|
|
10101
|
+
}
|
|
10102
|
+
async function uploadToSeverino(zipPath, severinoUrl, name, apiKey) {
|
|
10103
|
+
const deployUrl = `${severinoUrl.replace(/\/$/, "")}/api/v1/deploy`;
|
|
10104
|
+
const curlArgs = [
|
|
10105
|
+
"-X",
|
|
10106
|
+
"POST",
|
|
10107
|
+
deployUrl,
|
|
10108
|
+
"-F",
|
|
10109
|
+
`file=@${zipPath}`
|
|
10110
|
+
];
|
|
10111
|
+
if (name) {
|
|
10112
|
+
curlArgs.push("-F", `name=${name}`);
|
|
10113
|
+
}
|
|
10114
|
+
if (apiKey) {
|
|
10115
|
+
curlArgs.push("-H", `Authorization: Bearer ${apiKey}`);
|
|
10116
|
+
curlArgs.push("-H", `X-API-Key: ${apiKey}`);
|
|
10117
|
+
}
|
|
10118
|
+
curlArgs.push("-H", "Accept: application/json");
|
|
10119
|
+
const curlCommand = `curl ${curlArgs.join(" ")}`;
|
|
10120
|
+
const result = await shellCommand({
|
|
10121
|
+
command: curlCommand,
|
|
10122
|
+
timeout: 60
|
|
10123
|
+
// 1 minuto para upload
|
|
10124
|
+
});
|
|
10125
|
+
const resultJson = JSON.parse(result);
|
|
10126
|
+
if (resultJson.status !== "success") {
|
|
10127
|
+
throw new Error(`Upload failed: ${resultJson.stderr}`);
|
|
10128
|
+
}
|
|
10129
|
+
try {
|
|
10130
|
+
const response = JSON.parse(resultJson.stdout);
|
|
10131
|
+
if (response.success === false || response.error) {
|
|
10132
|
+
const errorMsg = typeof response.error === "string" ? response.error : response.error?.message || "Deploy failed";
|
|
10133
|
+
return {
|
|
10134
|
+
success: false,
|
|
10135
|
+
error: errorMsg
|
|
10136
|
+
};
|
|
10137
|
+
}
|
|
10138
|
+
const data = response.data || {};
|
|
10139
|
+
return {
|
|
10140
|
+
success: true,
|
|
10141
|
+
appId: data.appId,
|
|
10142
|
+
name: data.name,
|
|
10143
|
+
status: data.status || "building",
|
|
10144
|
+
url: severinoUrl.replace(/\/$/, "") + `/app/${data.appId}`,
|
|
10145
|
+
message: data.message || "Deploy iniciado"
|
|
10146
|
+
};
|
|
10147
|
+
} catch (parseError) {
|
|
10148
|
+
throw new Error(`Failed to parse response: ${parseError.message}`);
|
|
10149
|
+
}
|
|
10150
|
+
}
|
|
10151
|
+
async function deployApp(args) {
|
|
10152
|
+
const envSeverinoUrl = process.env.SEVERINO_URL || "http://localhost:3000";
|
|
10153
|
+
const envApiKey = process.env.SEVERINO_API_KEY || void 0;
|
|
10154
|
+
const {
|
|
10155
|
+
projectDir,
|
|
10156
|
+
name,
|
|
10157
|
+
severinoUrl = envSeverinoUrl,
|
|
10158
|
+
apiKey = envApiKey
|
|
10159
|
+
} = args;
|
|
10160
|
+
try {
|
|
10161
|
+
if (!projectDir || typeof projectDir !== "string") {
|
|
10162
|
+
return { success: false, error: "projectDir is required" };
|
|
10163
|
+
}
|
|
10164
|
+
const resolvedProjectDir = resolveWorkspacePath(projectDir);
|
|
10165
|
+
try {
|
|
10166
|
+
await fs24.access(resolvedProjectDir);
|
|
10167
|
+
} catch {
|
|
10168
|
+
return {
|
|
10169
|
+
success: false,
|
|
10170
|
+
error: `Project directory not found: ${resolvedProjectDir}`
|
|
10171
|
+
};
|
|
10172
|
+
}
|
|
10173
|
+
const packageJsonPath = path26.join(resolvedProjectDir, "package.json");
|
|
10174
|
+
try {
|
|
10175
|
+
await fs24.access(packageJsonPath);
|
|
10176
|
+
} catch {
|
|
10177
|
+
return {
|
|
10178
|
+
success: false,
|
|
10179
|
+
error: "Not a Next.js project: package.json not found"
|
|
10180
|
+
};
|
|
10181
|
+
}
|
|
10182
|
+
const packageJsonContent = await fs24.readFile(packageJsonPath, "utf-8");
|
|
10183
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
10184
|
+
const hasNext = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
10185
|
+
if (!hasNext) {
|
|
10186
|
+
return {
|
|
10187
|
+
success: false,
|
|
10188
|
+
error: "Not a Next.js project: next not found in dependencies"
|
|
10189
|
+
};
|
|
10190
|
+
}
|
|
10191
|
+
const appName = name || packageJson.name || path26.basename(resolvedProjectDir);
|
|
10192
|
+
const tempDir = path26.join(resolvedProjectDir, ".tmp");
|
|
10193
|
+
await fs24.mkdir(tempDir, { recursive: true });
|
|
10194
|
+
const zipPath = path26.join(tempDir, `${appName}-${Date.now()}.zip`);
|
|
10195
|
+
console.log(`[deploy-app] Creating ZIP: ${zipPath}`);
|
|
10196
|
+
await createProjectZip(resolvedProjectDir, zipPath);
|
|
10197
|
+
const zipStats = await fs24.stat(zipPath);
|
|
10198
|
+
const zipSizeMB = zipStats.size / 1024 / 1024;
|
|
10199
|
+
if (zipSizeMB > 50) {
|
|
10200
|
+
await fs24.unlink(zipPath).catch(() => {
|
|
10201
|
+
});
|
|
10202
|
+
await fs24.rmdir(tempDir).catch(() => {
|
|
10203
|
+
});
|
|
10204
|
+
return {
|
|
10205
|
+
success: false,
|
|
10206
|
+
error: `ZIP too large: ${zipSizeMB.toFixed(2)}MB (max: 50MB)`
|
|
10207
|
+
};
|
|
10208
|
+
}
|
|
10209
|
+
console.log(`[deploy-app] ZIP size: ${zipSizeMB.toFixed(2)}MB`);
|
|
10210
|
+
console.log(`[deploy-app] Uploading to ${severinoUrl}...`);
|
|
10211
|
+
const deployResult = await uploadToSeverino(zipPath, severinoUrl, appName, apiKey);
|
|
10212
|
+
try {
|
|
10213
|
+
await fs24.unlink(zipPath);
|
|
10214
|
+
await fs24.rmdir(tempDir);
|
|
10215
|
+
} catch (e) {
|
|
10216
|
+
console.warn("[deploy-app] Cleanup warning:", e);
|
|
10217
|
+
}
|
|
10218
|
+
if (deployResult.success) {
|
|
10219
|
+
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
10220
|
+
}
|
|
10221
|
+
return deployResult;
|
|
10222
|
+
} catch (error) {
|
|
10223
|
+
console.error("[deploy-app] Error:", error.message);
|
|
10224
|
+
return {
|
|
10225
|
+
success: false,
|
|
10226
|
+
error: error.message || String(error)
|
|
10227
|
+
};
|
|
10228
|
+
}
|
|
10229
|
+
}
|
|
10230
|
+
|
|
10231
|
+
// src/app/agent/runtime/native_tool_catalog.ts
|
|
10232
|
+
init_sandbox_policy();
|
|
9191
10233
|
var NATIVE_TOOL_ENTRIES = [
|
|
9192
10234
|
{
|
|
9193
10235
|
metadata: {
|
|
@@ -9751,6 +10793,28 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
9751
10793
|
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."
|
|
9752
10794
|
},
|
|
9753
10795
|
implementation: killAgent
|
|
10796
|
+
},
|
|
10797
|
+
{
|
|
10798
|
+
metadata: {
|
|
10799
|
+
name: "create_next_app",
|
|
10800
|
+
category: "filesystem",
|
|
10801
|
+
riskLevel: "write",
|
|
10802
|
+
autoApproveInLocal: false,
|
|
10803
|
+
autoApproveInSandbox: true,
|
|
10804
|
+
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)."
|
|
10805
|
+
},
|
|
10806
|
+
implementation: createNextApp
|
|
10807
|
+
},
|
|
10808
|
+
{
|
|
10809
|
+
metadata: {
|
|
10810
|
+
name: "deploy_app",
|
|
10811
|
+
category: "execution",
|
|
10812
|
+
riskLevel: "network",
|
|
10813
|
+
autoApproveInLocal: false,
|
|
10814
|
+
autoApproveInSandbox: true,
|
|
10815
|
+
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."
|
|
10816
|
+
},
|
|
10817
|
+
implementation: deployApp
|
|
9754
10818
|
}
|
|
9755
10819
|
];
|
|
9756
10820
|
var TOOL_METADATA_MAP = new Map(
|
|
@@ -9766,6 +10830,10 @@ function getNativeToolImplementation(toolName) {
|
|
|
9766
10830
|
return TOOL_IMPLEMENTATION_MAP.get(toolName);
|
|
9767
10831
|
}
|
|
9768
10832
|
function getAllNativeToolMetadata() {
|
|
10833
|
+
const policy = getSandboxPolicy();
|
|
10834
|
+
if (!policy.isSandbox) {
|
|
10835
|
+
return NATIVE_TOOL_ENTRIES.filter((entry) => entry.metadata.autoApproveInLocal !== false || !["create_next_app", "deploy_app"].includes(entry.metadata.name)).map((entry) => entry.metadata);
|
|
10836
|
+
}
|
|
9769
10837
|
return NATIVE_TOOL_ENTRIES.map((entry) => entry.metadata);
|
|
9770
10838
|
}
|
|
9771
10839
|
function applyMetadataToToolDefinitions(toolDefinitions) {
|
|
@@ -9791,9 +10859,9 @@ var ToolInvoker = class {
|
|
|
9791
10859
|
async initialize() {
|
|
9792
10860
|
try {
|
|
9793
10861
|
const __filename = fileURLToPath2(import.meta.url);
|
|
9794
|
-
const __dirname =
|
|
9795
|
-
const configPath =
|
|
9796
|
-
const fileContent = await
|
|
10862
|
+
const __dirname = path27.dirname(__filename);
|
|
10863
|
+
const configPath = path27.resolve(__dirname, "config", "native_tools.json");
|
|
10864
|
+
const fileContent = await fs25.readFile(configPath, "utf-8");
|
|
9797
10865
|
const config2 = JSON.parse(fileContent);
|
|
9798
10866
|
this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
|
|
9799
10867
|
} catch (error) {
|
|
@@ -9835,9 +10903,9 @@ var ToolInvoker = class {
|
|
|
9835
10903
|
};
|
|
9836
10904
|
|
|
9837
10905
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
9838
|
-
import { promises as
|
|
9839
|
-
import
|
|
9840
|
-
import
|
|
10906
|
+
import { promises as fs26 } from "fs";
|
|
10907
|
+
import path28 from "path";
|
|
10908
|
+
import os16 from "os";
|
|
9841
10909
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
9842
10910
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
9843
10911
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
@@ -9864,9 +10932,9 @@ var MCPClient = class {
|
|
|
9864
10932
|
});
|
|
9865
10933
|
}
|
|
9866
10934
|
const __filename = fileURLToPath3(import.meta.url);
|
|
9867
|
-
const __dirname =
|
|
9868
|
-
const defaultConfigPath =
|
|
9869
|
-
const userConfigPath =
|
|
10935
|
+
const __dirname = path28.dirname(__filename);
|
|
10936
|
+
const defaultConfigPath = path28.resolve(__dirname, "config", "bluma-mcp.json");
|
|
10937
|
+
const userConfigPath = path28.join(os16.homedir(), ".bluma", "bluma-mcp.json");
|
|
9870
10938
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
9871
10939
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
9872
10940
|
const mergedConfig = {
|
|
@@ -9900,7 +10968,7 @@ var MCPClient = class {
|
|
|
9900
10968
|
}
|
|
9901
10969
|
async loadMcpConfig(configPath, configType) {
|
|
9902
10970
|
try {
|
|
9903
|
-
const fileContent = await
|
|
10971
|
+
const fileContent = await fs26.readFile(configPath, "utf-8");
|
|
9904
10972
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
9905
10973
|
return JSON.parse(processedContent);
|
|
9906
10974
|
} catch (error) {
|
|
@@ -9919,7 +10987,7 @@ var MCPClient = class {
|
|
|
9919
10987
|
async connectToStdioServer(serverName, config2) {
|
|
9920
10988
|
let commandToExecute = config2.command;
|
|
9921
10989
|
let argsToExecute = config2.args || [];
|
|
9922
|
-
const isWindows =
|
|
10990
|
+
const isWindows = os16.platform() === "win32";
|
|
9923
10991
|
if (!isWindows && commandToExecute.toLowerCase() === "cmd") {
|
|
9924
10992
|
if (argsToExecute.length >= 2 && argsToExecute[0].toLowerCase() === "/c") {
|
|
9925
10993
|
commandToExecute = argsToExecute[1];
|
|
@@ -10077,13 +11145,13 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
10077
11145
|
};
|
|
10078
11146
|
|
|
10079
11147
|
// src/app/agent/bluma/core/bluma.ts
|
|
10080
|
-
import
|
|
11148
|
+
import path37 from "path";
|
|
10081
11149
|
import { v4 as uuidv48 } from "uuid";
|
|
10082
11150
|
|
|
10083
11151
|
// src/app/agent/session_manager/session_manager.ts
|
|
10084
|
-
import
|
|
10085
|
-
import
|
|
10086
|
-
import { promises as
|
|
11152
|
+
import path29 from "path";
|
|
11153
|
+
import os17 from "os";
|
|
11154
|
+
import { promises as fs27 } from "fs";
|
|
10087
11155
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
10088
11156
|
async function withFileLock(file, fn) {
|
|
10089
11157
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -10119,13 +11187,13 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
10119
11187
|
function expandHome(p) {
|
|
10120
11188
|
if (!p) return p;
|
|
10121
11189
|
if (p.startsWith("~")) {
|
|
10122
|
-
return
|
|
11190
|
+
return path29.join(os17.homedir(), p.slice(1));
|
|
10123
11191
|
}
|
|
10124
11192
|
return p;
|
|
10125
11193
|
}
|
|
10126
11194
|
function getPreferredAppDir() {
|
|
10127
|
-
const fixed =
|
|
10128
|
-
return
|
|
11195
|
+
const fixed = path29.join(os17.homedir(), ".bluma");
|
|
11196
|
+
return path29.resolve(expandHome(fixed));
|
|
10129
11197
|
}
|
|
10130
11198
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
10131
11199
|
let attempt = 0;
|
|
@@ -10133,10 +11201,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10133
11201
|
const isWin = process.platform === "win32";
|
|
10134
11202
|
while (attempt <= maxRetries) {
|
|
10135
11203
|
try {
|
|
10136
|
-
const dir =
|
|
10137
|
-
await
|
|
11204
|
+
const dir = path29.dirname(dest);
|
|
11205
|
+
await fs27.mkdir(dir, { recursive: true }).catch(() => {
|
|
10138
11206
|
});
|
|
10139
|
-
await
|
|
11207
|
+
await fs27.rename(src, dest);
|
|
10140
11208
|
return;
|
|
10141
11209
|
} catch (e) {
|
|
10142
11210
|
lastErr = e;
|
|
@@ -10149,13 +11217,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10149
11217
|
}
|
|
10150
11218
|
}
|
|
10151
11219
|
try {
|
|
10152
|
-
await
|
|
10153
|
-
const data = await
|
|
10154
|
-
const dir =
|
|
10155
|
-
await
|
|
11220
|
+
await fs27.access(src);
|
|
11221
|
+
const data = await fs27.readFile(src);
|
|
11222
|
+
const dir = path29.dirname(dest);
|
|
11223
|
+
await fs27.mkdir(dir, { recursive: true }).catch(() => {
|
|
10156
11224
|
});
|
|
10157
|
-
await
|
|
10158
|
-
await
|
|
11225
|
+
await fs27.writeFile(dest, data);
|
|
11226
|
+
await fs27.unlink(src).catch(() => {
|
|
10159
11227
|
});
|
|
10160
11228
|
return;
|
|
10161
11229
|
} catch (fallbackErr) {
|
|
@@ -10168,16 +11236,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
10168
11236
|
}
|
|
10169
11237
|
async function ensureSessionDir() {
|
|
10170
11238
|
const appDir = getPreferredAppDir();
|
|
10171
|
-
const sessionDir =
|
|
10172
|
-
await
|
|
11239
|
+
const sessionDir = path29.join(appDir, "sessions");
|
|
11240
|
+
await fs27.mkdir(sessionDir, { recursive: true });
|
|
10173
11241
|
return sessionDir;
|
|
10174
11242
|
}
|
|
10175
11243
|
async function loadOrcreateSession(sessionId) {
|
|
10176
11244
|
const sessionDir = await ensureSessionDir();
|
|
10177
|
-
const sessionFile =
|
|
11245
|
+
const sessionFile = path29.join(sessionDir, `${sessionId}.json`);
|
|
10178
11246
|
try {
|
|
10179
|
-
await
|
|
10180
|
-
const fileContent = await
|
|
11247
|
+
await fs27.access(sessionFile);
|
|
11248
|
+
const fileContent = await fs27.readFile(sessionFile, "utf-8");
|
|
10181
11249
|
const sessionData = JSON.parse(fileContent);
|
|
10182
11250
|
const memory = {
|
|
10183
11251
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -10190,7 +11258,7 @@ async function loadOrcreateSession(sessionId) {
|
|
|
10190
11258
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10191
11259
|
conversation_history: []
|
|
10192
11260
|
};
|
|
10193
|
-
await
|
|
11261
|
+
await fs27.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
10194
11262
|
const emptyMemory = {
|
|
10195
11263
|
historyAnchor: null,
|
|
10196
11264
|
compressedTurnSliceCount: 0
|
|
@@ -10202,12 +11270,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10202
11270
|
await withFileLock(sessionFile, async () => {
|
|
10203
11271
|
let sessionData;
|
|
10204
11272
|
try {
|
|
10205
|
-
const dir =
|
|
10206
|
-
await
|
|
11273
|
+
const dir = path29.dirname(sessionFile);
|
|
11274
|
+
await fs27.mkdir(dir, { recursive: true });
|
|
10207
11275
|
} catch {
|
|
10208
11276
|
}
|
|
10209
11277
|
try {
|
|
10210
|
-
const fileContent = await
|
|
11278
|
+
const fileContent = await fs27.readFile(sessionFile, "utf-8");
|
|
10211
11279
|
sessionData = JSON.parse(fileContent);
|
|
10212
11280
|
} catch (error) {
|
|
10213
11281
|
const code = error && error.code;
|
|
@@ -10218,14 +11286,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10218
11286
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
10219
11287
|
}
|
|
10220
11288
|
}
|
|
10221
|
-
const sessionId =
|
|
11289
|
+
const sessionId = path29.basename(sessionFile, ".json");
|
|
10222
11290
|
sessionData = {
|
|
10223
11291
|
session_id: sessionId,
|
|
10224
11292
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10225
11293
|
conversation_history: []
|
|
10226
11294
|
};
|
|
10227
11295
|
try {
|
|
10228
|
-
await
|
|
11296
|
+
await fs27.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
10229
11297
|
} catch {
|
|
10230
11298
|
}
|
|
10231
11299
|
}
|
|
@@ -10241,7 +11309,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10241
11309
|
}
|
|
10242
11310
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
10243
11311
|
try {
|
|
10244
|
-
await
|
|
11312
|
+
await fs27.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
10245
11313
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
10246
11314
|
} catch (writeError) {
|
|
10247
11315
|
if (writeError instanceof Error) {
|
|
@@ -10250,7 +11318,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
10250
11318
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
10251
11319
|
}
|
|
10252
11320
|
try {
|
|
10253
|
-
await
|
|
11321
|
+
await fs27.unlink(tempSessionFile);
|
|
10254
11322
|
} catch {
|
|
10255
11323
|
}
|
|
10256
11324
|
}
|
|
@@ -10267,15 +11335,15 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
10267
11335
|
}
|
|
10268
11336
|
|
|
10269
11337
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
10270
|
-
import
|
|
10271
|
-
import
|
|
10272
|
-
import
|
|
11338
|
+
import os21 from "os";
|
|
11339
|
+
import fs32 from "fs";
|
|
11340
|
+
import path34 from "path";
|
|
10273
11341
|
import { execSync as execSync3 } from "child_process";
|
|
10274
11342
|
|
|
10275
11343
|
// src/app/agent/skills/skill_loader.ts
|
|
10276
|
-
import
|
|
10277
|
-
import
|
|
10278
|
-
import
|
|
11344
|
+
import fs28 from "fs";
|
|
11345
|
+
import path30 from "path";
|
|
11346
|
+
import os18 from "os";
|
|
10279
11347
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10280
11348
|
var SkillLoader = class _SkillLoader {
|
|
10281
11349
|
bundledSkillsDir;
|
|
@@ -10284,8 +11352,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10284
11352
|
cache = /* @__PURE__ */ new Map();
|
|
10285
11353
|
conflicts = [];
|
|
10286
11354
|
constructor(projectRoot, bundledDir) {
|
|
10287
|
-
this.projectSkillsDir =
|
|
10288
|
-
this.globalSkillsDir =
|
|
11355
|
+
this.projectSkillsDir = path30.join(projectRoot, ".bluma", "skills");
|
|
11356
|
+
this.globalSkillsDir = path30.join(os18.homedir(), ".bluma", "skills");
|
|
10289
11357
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
10290
11358
|
}
|
|
10291
11359
|
/**
|
|
@@ -10294,48 +11362,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
10294
11362
|
*/
|
|
10295
11363
|
static resolveBundledDir() {
|
|
10296
11364
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
10297
|
-
return
|
|
11365
|
+
return path30.join(process.cwd(), "dist", "config", "skills");
|
|
10298
11366
|
}
|
|
10299
11367
|
const candidates = [];
|
|
10300
11368
|
const push = (p) => {
|
|
10301
|
-
const abs =
|
|
11369
|
+
const abs = path30.resolve(p);
|
|
10302
11370
|
if (!candidates.includes(abs)) {
|
|
10303
11371
|
candidates.push(abs);
|
|
10304
11372
|
}
|
|
10305
11373
|
};
|
|
10306
11374
|
let argvBundled = null;
|
|
10307
11375
|
try {
|
|
10308
|
-
const bundleDir =
|
|
10309
|
-
push(
|
|
11376
|
+
const bundleDir = path30.dirname(fileURLToPath4(import.meta.url));
|
|
11377
|
+
push(path30.join(bundleDir, "config", "skills"));
|
|
10310
11378
|
} catch {
|
|
10311
11379
|
}
|
|
10312
11380
|
const argv1 = process.argv[1];
|
|
10313
11381
|
if (argv1 && !argv1.startsWith("-")) {
|
|
10314
11382
|
try {
|
|
10315
11383
|
let resolved = argv1;
|
|
10316
|
-
if (
|
|
10317
|
-
resolved =
|
|
10318
|
-
} else if (!
|
|
10319
|
-
resolved =
|
|
11384
|
+
if (path30.isAbsolute(argv1) && fs28.existsSync(argv1)) {
|
|
11385
|
+
resolved = fs28.realpathSync(argv1);
|
|
11386
|
+
} else if (!path30.isAbsolute(argv1)) {
|
|
11387
|
+
resolved = path30.resolve(process.cwd(), argv1);
|
|
10320
11388
|
}
|
|
10321
|
-
const scriptDir =
|
|
10322
|
-
argvBundled =
|
|
11389
|
+
const scriptDir = path30.dirname(resolved);
|
|
11390
|
+
argvBundled = path30.join(scriptDir, "config", "skills");
|
|
10323
11391
|
push(argvBundled);
|
|
10324
11392
|
} catch {
|
|
10325
11393
|
}
|
|
10326
11394
|
}
|
|
10327
11395
|
for (const abs of candidates) {
|
|
10328
|
-
if (
|
|
11396
|
+
if (fs28.existsSync(abs)) {
|
|
10329
11397
|
return abs;
|
|
10330
11398
|
}
|
|
10331
11399
|
}
|
|
10332
11400
|
try {
|
|
10333
|
-
return
|
|
11401
|
+
return path30.join(path30.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
10334
11402
|
} catch {
|
|
10335
11403
|
if (argvBundled) {
|
|
10336
11404
|
return argvBundled;
|
|
10337
11405
|
}
|
|
10338
|
-
return
|
|
11406
|
+
return path30.join(os18.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
10339
11407
|
}
|
|
10340
11408
|
}
|
|
10341
11409
|
/**
|
|
@@ -10364,8 +11432,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10364
11432
|
this.conflicts.push({
|
|
10365
11433
|
name: skill.name,
|
|
10366
11434
|
userSource: source,
|
|
10367
|
-
userPath:
|
|
10368
|
-
bundledPath:
|
|
11435
|
+
userPath: path30.join(dir, skill.name, "SKILL.md"),
|
|
11436
|
+
bundledPath: path30.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
10369
11437
|
});
|
|
10370
11438
|
continue;
|
|
10371
11439
|
}
|
|
@@ -10373,20 +11441,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
10373
11441
|
}
|
|
10374
11442
|
}
|
|
10375
11443
|
listFromDir(dir, source) {
|
|
10376
|
-
if (!
|
|
11444
|
+
if (!fs28.existsSync(dir)) return [];
|
|
10377
11445
|
try {
|
|
10378
|
-
return
|
|
10379
|
-
const fullPath =
|
|
10380
|
-
return
|
|
10381
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
11446
|
+
return fs28.readdirSync(dir).filter((d) => {
|
|
11447
|
+
const fullPath = path30.join(dir, d);
|
|
11448
|
+
return fs28.statSync(fullPath).isDirectory() && fs28.existsSync(path30.join(fullPath, "SKILL.md"));
|
|
11449
|
+
}).map((d) => this.loadMetadataFromPath(path30.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
10382
11450
|
} catch {
|
|
10383
11451
|
return [];
|
|
10384
11452
|
}
|
|
10385
11453
|
}
|
|
10386
11454
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
10387
|
-
if (!
|
|
11455
|
+
if (!fs28.existsSync(skillPath)) return null;
|
|
10388
11456
|
try {
|
|
10389
|
-
const raw =
|
|
11457
|
+
const raw = fs28.readFileSync(skillPath, "utf-8");
|
|
10390
11458
|
const parsed = this.parseFrontmatter(raw);
|
|
10391
11459
|
return {
|
|
10392
11460
|
name: parsed.name || skillName,
|
|
@@ -10408,12 +11476,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
10408
11476
|
*/
|
|
10409
11477
|
load(name) {
|
|
10410
11478
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
10411
|
-
const bundledPath =
|
|
10412
|
-
const projectPath =
|
|
10413
|
-
const globalPath =
|
|
10414
|
-
const existsBundled =
|
|
10415
|
-
const existsProject =
|
|
10416
|
-
const existsGlobal =
|
|
11479
|
+
const bundledPath = path30.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
11480
|
+
const projectPath = path30.join(this.projectSkillsDir, name, "SKILL.md");
|
|
11481
|
+
const globalPath = path30.join(this.globalSkillsDir, name, "SKILL.md");
|
|
11482
|
+
const existsBundled = fs28.existsSync(bundledPath);
|
|
11483
|
+
const existsProject = fs28.existsSync(projectPath);
|
|
11484
|
+
const existsGlobal = fs28.existsSync(globalPath);
|
|
10417
11485
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
10418
11486
|
const conflictSource = existsProject ? "project" : "global";
|
|
10419
11487
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -10452,9 +11520,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
10452
11520
|
}
|
|
10453
11521
|
loadFromPath(skillPath, name, source) {
|
|
10454
11522
|
try {
|
|
10455
|
-
const raw =
|
|
11523
|
+
const raw = fs28.readFileSync(skillPath, "utf-8");
|
|
10456
11524
|
const parsed = this.parseFrontmatter(raw);
|
|
10457
|
-
const skillDir =
|
|
11525
|
+
const skillDir = path30.dirname(skillPath);
|
|
10458
11526
|
return {
|
|
10459
11527
|
name: parsed.name || name,
|
|
10460
11528
|
description: parsed.description || "",
|
|
@@ -10463,22 +11531,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
10463
11531
|
version: parsed.version,
|
|
10464
11532
|
author: parsed.author,
|
|
10465
11533
|
license: parsed.license,
|
|
10466
|
-
references: this.scanAssets(
|
|
10467
|
-
scripts: this.scanAssets(
|
|
11534
|
+
references: this.scanAssets(path30.join(skillDir, "references")),
|
|
11535
|
+
scripts: this.scanAssets(path30.join(skillDir, "scripts"))
|
|
10468
11536
|
};
|
|
10469
11537
|
} catch {
|
|
10470
11538
|
return null;
|
|
10471
11539
|
}
|
|
10472
11540
|
}
|
|
10473
11541
|
scanAssets(dir) {
|
|
10474
|
-
if (!
|
|
11542
|
+
if (!fs28.existsSync(dir)) return [];
|
|
10475
11543
|
try {
|
|
10476
|
-
return
|
|
10477
|
-
const fp =
|
|
10478
|
-
return
|
|
11544
|
+
return fs28.readdirSync(dir).filter((f) => {
|
|
11545
|
+
const fp = path30.join(dir, f);
|
|
11546
|
+
return fs28.statSync(fp).isFile();
|
|
10479
11547
|
}).map((f) => ({
|
|
10480
11548
|
name: f,
|
|
10481
|
-
path:
|
|
11549
|
+
path: path30.resolve(dir, f)
|
|
10482
11550
|
}));
|
|
10483
11551
|
} catch {
|
|
10484
11552
|
return [];
|
|
@@ -10535,10 +11603,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
10535
11603
|
this.cache.clear();
|
|
10536
11604
|
}
|
|
10537
11605
|
exists(name) {
|
|
10538
|
-
const bundledPath =
|
|
10539
|
-
const projectPath =
|
|
10540
|
-
const globalPath =
|
|
10541
|
-
return
|
|
11606
|
+
const bundledPath = path30.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
11607
|
+
const projectPath = path30.join(this.projectSkillsDir, name, "SKILL.md");
|
|
11608
|
+
const globalPath = path30.join(this.globalSkillsDir, name, "SKILL.md");
|
|
11609
|
+
return fs28.existsSync(bundledPath) || fs28.existsSync(projectPath) || fs28.existsSync(globalPath);
|
|
10542
11610
|
}
|
|
10543
11611
|
/**
|
|
10544
11612
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -10570,8 +11638,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
10570
11638
|
};
|
|
10571
11639
|
|
|
10572
11640
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
10573
|
-
import
|
|
10574
|
-
import
|
|
11641
|
+
import fs29 from "fs";
|
|
11642
|
+
import path31 from "path";
|
|
10575
11643
|
import { execSync as execSync2 } from "child_process";
|
|
10576
11644
|
var LIMITS = {
|
|
10577
11645
|
readme: 1e4,
|
|
@@ -10586,10 +11654,10 @@ var LIMITS = {
|
|
|
10586
11654
|
};
|
|
10587
11655
|
function safeReadFile(filePath, maxChars) {
|
|
10588
11656
|
try {
|
|
10589
|
-
if (!
|
|
10590
|
-
const st =
|
|
11657
|
+
if (!fs29.existsSync(filePath)) return null;
|
|
11658
|
+
const st = fs29.statSync(filePath);
|
|
10591
11659
|
if (!st.isFile()) return null;
|
|
10592
|
-
const raw =
|
|
11660
|
+
const raw = fs29.readFileSync(filePath, "utf8");
|
|
10593
11661
|
if (raw.length <= maxChars) return raw;
|
|
10594
11662
|
return `${raw.slice(0, maxChars)}
|
|
10595
11663
|
|
|
@@ -10600,7 +11668,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
10600
11668
|
}
|
|
10601
11669
|
function tryReadReadme(cwd) {
|
|
10602
11670
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
10603
|
-
const c = safeReadFile(
|
|
11671
|
+
const c = safeReadFile(path31.join(cwd, name), LIMITS.readme);
|
|
10604
11672
|
if (c) return `(${name})
|
|
10605
11673
|
${c}`;
|
|
10606
11674
|
}
|
|
@@ -10608,14 +11676,14 @@ ${c}`;
|
|
|
10608
11676
|
}
|
|
10609
11677
|
function tryReadBluMaMd(cwd) {
|
|
10610
11678
|
const paths = [
|
|
10611
|
-
|
|
10612
|
-
|
|
10613
|
-
|
|
11679
|
+
path31.join(cwd, "BluMa.md"),
|
|
11680
|
+
path31.join(cwd, "BLUMA.md"),
|
|
11681
|
+
path31.join(cwd, ".bluma", "BluMa.md")
|
|
10614
11682
|
];
|
|
10615
11683
|
for (const p of paths) {
|
|
10616
11684
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
10617
11685
|
if (c) {
|
|
10618
|
-
const rel =
|
|
11686
|
+
const rel = path31.relative(cwd, p) || p;
|
|
10619
11687
|
return `(${rel})
|
|
10620
11688
|
${c}`;
|
|
10621
11689
|
}
|
|
@@ -10623,10 +11691,10 @@ ${c}`;
|
|
|
10623
11691
|
return null;
|
|
10624
11692
|
}
|
|
10625
11693
|
function summarizePackageJson(cwd) {
|
|
10626
|
-
const p =
|
|
11694
|
+
const p = path31.join(cwd, "package.json");
|
|
10627
11695
|
try {
|
|
10628
|
-
if (!
|
|
10629
|
-
const pkg = JSON.parse(
|
|
11696
|
+
if (!fs29.existsSync(p)) return null;
|
|
11697
|
+
const pkg = JSON.parse(fs29.readFileSync(p, "utf8"));
|
|
10630
11698
|
const scripts = pkg.scripts;
|
|
10631
11699
|
let scriptKeys = "";
|
|
10632
11700
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -10659,7 +11727,7 @@ function summarizePackageJson(cwd) {
|
|
|
10659
11727
|
}
|
|
10660
11728
|
function topLevelListing(cwd) {
|
|
10661
11729
|
try {
|
|
10662
|
-
const names =
|
|
11730
|
+
const names = fs29.readdirSync(cwd, { withFileTypes: true });
|
|
10663
11731
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
10664
11732
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
10665
11733
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -10717,7 +11785,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
10717
11785
|
parts.push(pkg);
|
|
10718
11786
|
parts.push("```\n");
|
|
10719
11787
|
}
|
|
10720
|
-
const py = safeReadFile(
|
|
11788
|
+
const py = safeReadFile(path31.join(cwd, "pyproject.toml"), LIMITS.pyproject);
|
|
10721
11789
|
if (py) {
|
|
10722
11790
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
10723
11791
|
parts.push(py);
|
|
@@ -10735,15 +11803,15 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
10735
11803
|
parts.push(bluma);
|
|
10736
11804
|
parts.push("```\n");
|
|
10737
11805
|
}
|
|
10738
|
-
const contrib = safeReadFile(
|
|
11806
|
+
const contrib = safeReadFile(path31.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
10739
11807
|
if (contrib) {
|
|
10740
11808
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
10741
11809
|
parts.push(contrib);
|
|
10742
11810
|
parts.push("```\n");
|
|
10743
11811
|
}
|
|
10744
|
-
const chlog = safeReadFile(
|
|
11812
|
+
const chlog = safeReadFile(path31.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
|
|
10745
11813
|
if (!chlog) {
|
|
10746
|
-
const alt = safeReadFile(
|
|
11814
|
+
const alt = safeReadFile(path31.join(cwd, "CHANGES.md"), LIMITS.changelog);
|
|
10747
11815
|
if (alt) {
|
|
10748
11816
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
10749
11817
|
parts.push(alt);
|
|
@@ -10783,15 +11851,15 @@ init_runtime_config();
|
|
|
10783
11851
|
|
|
10784
11852
|
// src/app/agent/runtime/plugin_registry.ts
|
|
10785
11853
|
init_sandbox_policy();
|
|
10786
|
-
import
|
|
10787
|
-
import
|
|
10788
|
-
import
|
|
11854
|
+
import fs30 from "fs";
|
|
11855
|
+
import os19 from "os";
|
|
11856
|
+
import path32 from "path";
|
|
10789
11857
|
function getProjectPluginsDir() {
|
|
10790
11858
|
const policy = getSandboxPolicy();
|
|
10791
|
-
return
|
|
11859
|
+
return path32.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
10792
11860
|
}
|
|
10793
11861
|
function getGlobalPluginsDir() {
|
|
10794
|
-
return
|
|
11862
|
+
return path32.join(process.env.HOME || os19.homedir(), ".bluma", "plugins");
|
|
10795
11863
|
}
|
|
10796
11864
|
function getPluginDirs() {
|
|
10797
11865
|
return {
|
|
@@ -10800,11 +11868,11 @@ function getPluginDirs() {
|
|
|
10800
11868
|
};
|
|
10801
11869
|
}
|
|
10802
11870
|
function readManifest(manifestPath, fallbackName) {
|
|
10803
|
-
if (!
|
|
11871
|
+
if (!fs30.existsSync(manifestPath)) {
|
|
10804
11872
|
return null;
|
|
10805
11873
|
}
|
|
10806
11874
|
try {
|
|
10807
|
-
const parsed = JSON.parse(
|
|
11875
|
+
const parsed = JSON.parse(fs30.readFileSync(manifestPath, "utf-8"));
|
|
10808
11876
|
return {
|
|
10809
11877
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
10810
11878
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -10817,22 +11885,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
10817
11885
|
}
|
|
10818
11886
|
function findManifestPath(pluginDir) {
|
|
10819
11887
|
const candidates = [
|
|
10820
|
-
|
|
10821
|
-
|
|
11888
|
+
path32.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
11889
|
+
path32.join(pluginDir, "plugin.json")
|
|
10822
11890
|
];
|
|
10823
11891
|
for (const candidate of candidates) {
|
|
10824
|
-
if (
|
|
11892
|
+
if (fs30.existsSync(candidate)) {
|
|
10825
11893
|
return candidate;
|
|
10826
11894
|
}
|
|
10827
11895
|
}
|
|
10828
11896
|
return null;
|
|
10829
11897
|
}
|
|
10830
11898
|
function listFromDir(baseDir, source) {
|
|
10831
|
-
if (!
|
|
11899
|
+
if (!fs30.existsSync(baseDir)) {
|
|
10832
11900
|
return [];
|
|
10833
11901
|
}
|
|
10834
|
-
return
|
|
10835
|
-
const pluginDir =
|
|
11902
|
+
return fs30.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
11903
|
+
const pluginDir = path32.join(baseDir, entry.name);
|
|
10836
11904
|
const manifestPath = findManifestPath(pluginDir);
|
|
10837
11905
|
if (!manifestPath) {
|
|
10838
11906
|
return [];
|
|
@@ -11182,9 +12250,9 @@ function disableCoordinatorMode() {
|
|
|
11182
12250
|
}
|
|
11183
12251
|
|
|
11184
12252
|
// src/app/agent/utils/blumamd.ts
|
|
11185
|
-
import
|
|
11186
|
-
import
|
|
11187
|
-
import
|
|
12253
|
+
import fs31 from "fs";
|
|
12254
|
+
import path33 from "path";
|
|
12255
|
+
import os20 from "os";
|
|
11188
12256
|
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.";
|
|
11189
12257
|
var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
11190
12258
|
".md",
|
|
@@ -11312,12 +12380,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
11312
12380
|
function expandIncludePath(includePath, baseDir) {
|
|
11313
12381
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
11314
12382
|
if (cleanPath.startsWith("~")) {
|
|
11315
|
-
return
|
|
12383
|
+
return path33.join(os20.homedir(), cleanPath.slice(1));
|
|
11316
12384
|
}
|
|
11317
|
-
if (
|
|
12385
|
+
if (path33.isAbsolute(cleanPath)) {
|
|
11318
12386
|
return cleanPath;
|
|
11319
12387
|
}
|
|
11320
|
-
return
|
|
12388
|
+
return path33.resolve(baseDir, cleanPath);
|
|
11321
12389
|
}
|
|
11322
12390
|
function processIncludes(content, baseDir, processedFiles) {
|
|
11323
12391
|
const lines = content.split("\n");
|
|
@@ -11326,20 +12394,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11326
12394
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
11327
12395
|
if (includeMatch) {
|
|
11328
12396
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
11329
|
-
const normalizedPath =
|
|
12397
|
+
const normalizedPath = path33.normalize(includePath);
|
|
11330
12398
|
if (processedFiles.has(normalizedPath)) {
|
|
11331
12399
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
11332
12400
|
continue;
|
|
11333
12401
|
}
|
|
11334
|
-
const ext =
|
|
12402
|
+
const ext = path33.extname(includePath).toLowerCase();
|
|
11335
12403
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
11336
12404
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
11337
12405
|
continue;
|
|
11338
12406
|
}
|
|
11339
12407
|
try {
|
|
11340
|
-
const includedContent =
|
|
12408
|
+
const includedContent = fs31.readFileSync(includePath, "utf-8");
|
|
11341
12409
|
processedFiles.add(normalizedPath);
|
|
11342
|
-
const processedContent = processIncludes(includedContent,
|
|
12410
|
+
const processedContent = processIncludes(includedContent, path33.dirname(includePath), processedFiles);
|
|
11343
12411
|
result.push(`
|
|
11344
12412
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
11345
12413
|
`);
|
|
@@ -11358,9 +12426,9 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11358
12426
|
}
|
|
11359
12427
|
function readMemoryFile(filePath, type, priority) {
|
|
11360
12428
|
try {
|
|
11361
|
-
const content =
|
|
11362
|
-
const baseDir =
|
|
11363
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
12429
|
+
const content = fs31.readFileSync(filePath, "utf-8");
|
|
12430
|
+
const baseDir = path33.dirname(filePath);
|
|
12431
|
+
const processedFiles = /* @__PURE__ */ new Set([path33.normalize(filePath)]);
|
|
11364
12432
|
const processedContent = processIncludes(content, baseDir, processedFiles);
|
|
11365
12433
|
return {
|
|
11366
12434
|
path: filePath,
|
|
@@ -11377,33 +12445,33 @@ function readMemoryFile(filePath, type, priority) {
|
|
|
11377
12445
|
}
|
|
11378
12446
|
function findGitRoot(startDir) {
|
|
11379
12447
|
let current = startDir;
|
|
11380
|
-
while (current !==
|
|
11381
|
-
const gitPath =
|
|
12448
|
+
while (current !== path33.dirname(current)) {
|
|
12449
|
+
const gitPath = path33.join(current, ".git");
|
|
11382
12450
|
try {
|
|
11383
|
-
if (
|
|
12451
|
+
if (fs31.existsSync(gitPath)) {
|
|
11384
12452
|
return current;
|
|
11385
12453
|
}
|
|
11386
12454
|
} catch {
|
|
11387
12455
|
}
|
|
11388
|
-
current =
|
|
12456
|
+
current = path33.dirname(current);
|
|
11389
12457
|
}
|
|
11390
12458
|
return null;
|
|
11391
12459
|
}
|
|
11392
12460
|
function loadUserMemory() {
|
|
11393
12461
|
const files = [];
|
|
11394
|
-
const homeDir =
|
|
11395
|
-
const userBlumaDir =
|
|
11396
|
-
const userBlumaMd =
|
|
12462
|
+
const homeDir = os20.homedir();
|
|
12463
|
+
const userBlumaDir = path33.join(homeDir, ".bluma");
|
|
12464
|
+
const userBlumaMd = path33.join(userBlumaDir, "BLUMA.md");
|
|
11397
12465
|
const userFile = readMemoryFile(userBlumaMd, "user", 2);
|
|
11398
12466
|
if (userFile) {
|
|
11399
12467
|
files.push(userFile);
|
|
11400
12468
|
}
|
|
11401
|
-
const userRulesDir =
|
|
11402
|
-
if (
|
|
12469
|
+
const userRulesDir = path33.join(userBlumaDir, "rules");
|
|
12470
|
+
if (fs31.existsSync(userRulesDir)) {
|
|
11403
12471
|
try {
|
|
11404
|
-
const ruleFiles =
|
|
12472
|
+
const ruleFiles = fs31.readdirSync(userRulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
11405
12473
|
for (const ruleFile of ruleFiles) {
|
|
11406
|
-
const rulePath =
|
|
12474
|
+
const rulePath = path33.join(userRulesDir, ruleFile);
|
|
11407
12475
|
const rule = readMemoryFile(rulePath, "rule", 2);
|
|
11408
12476
|
if (rule) {
|
|
11409
12477
|
files.push(rule);
|
|
@@ -11417,22 +12485,22 @@ function loadUserMemory() {
|
|
|
11417
12485
|
function loadProjectMemory(cwd) {
|
|
11418
12486
|
const files = [];
|
|
11419
12487
|
const gitRoot = findGitRoot(cwd) || cwd;
|
|
11420
|
-
const projectBlumaMd =
|
|
12488
|
+
const projectBlumaMd = path33.join(gitRoot, "BLUMA.md");
|
|
11421
12489
|
const projectFile = readMemoryFile(projectBlumaMd, "project", 3);
|
|
11422
12490
|
if (projectFile) {
|
|
11423
12491
|
files.push(projectFile);
|
|
11424
12492
|
}
|
|
11425
|
-
const blumaDirBlumaMd =
|
|
12493
|
+
const blumaDirBlumaMd = path33.join(gitRoot, ".bluma", "BLUMA.md");
|
|
11426
12494
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "project", 3);
|
|
11427
12495
|
if (blumaDirFile) {
|
|
11428
12496
|
files.push(blumaDirFile);
|
|
11429
12497
|
}
|
|
11430
|
-
const rulesDir =
|
|
11431
|
-
if (
|
|
12498
|
+
const rulesDir = path33.join(gitRoot, ".bluma", "rules");
|
|
12499
|
+
if (fs31.existsSync(rulesDir)) {
|
|
11432
12500
|
try {
|
|
11433
|
-
const ruleFiles =
|
|
12501
|
+
const ruleFiles = fs31.readdirSync(rulesDir).filter((f) => f.endsWith(".md")).sort();
|
|
11434
12502
|
for (const ruleFile of ruleFiles) {
|
|
11435
|
-
const rulePath =
|
|
12503
|
+
const rulePath = path33.join(rulesDir, ruleFile);
|
|
11436
12504
|
const rule = readMemoryFile(rulePath, "rule", 3);
|
|
11437
12505
|
if (rule) {
|
|
11438
12506
|
files.push(rule);
|
|
@@ -11441,7 +12509,7 @@ function loadProjectMemory(cwd) {
|
|
|
11441
12509
|
} catch {
|
|
11442
12510
|
}
|
|
11443
12511
|
}
|
|
11444
|
-
const localBlumaMd =
|
|
12512
|
+
const localBlumaMd = path33.join(gitRoot, "BLUMA.local.md");
|
|
11445
12513
|
const localFile = readMemoryFile(localBlumaMd, "local", 4);
|
|
11446
12514
|
if (localFile) {
|
|
11447
12515
|
files.push(localFile);
|
|
@@ -11562,6 +12630,149 @@ Since you are in an **isolated sandbox**, ALL tools are auto-approved:
|
|
|
11562
12630
|
|
|
11563
12631
|
---
|
|
11564
12632
|
|
|
12633
|
+
## \u{1F680} NEXT.JS DEPLOY WORKFLOW - SEVERINO INTEGRATION
|
|
12634
|
+
|
|
12635
|
+
You have access to **two specialized tools** for creating and deploying Next.js apps to Severino:
|
|
12636
|
+
|
|
12637
|
+
### Tool 1: \\\`create_next_app\\\`
|
|
12638
|
+
|
|
12639
|
+
**Purpose:** Instant scaffold of a complete Next.js project with App Router, shadcn/ui, and Tailwind CSS.
|
|
12640
|
+
|
|
12641
|
+
**Args:**
|
|
12642
|
+
- \\\`name\\\` (required): Project name (will be directory name)
|
|
12643
|
+
- \\\`template\\\` (optional): \\\`'minimal'\\\` or \\\`'full'\\\` (default: \\\`'full'\\\`)
|
|
12644
|
+
- \\\`directory\\\` (optional): Where to create (default: workspace root)
|
|
12645
|
+
|
|
12646
|
+
**Example:**
|
|
12647
|
+
\\\`\\\`\\\`typescript
|
|
12648
|
+
const result = await createNextApp({
|
|
12649
|
+
name: 'erp-dashboard',
|
|
12650
|
+
template: 'full',
|
|
12651
|
+
});
|
|
12652
|
+
// Creates: /workspace/session_id/erp-dashboard/
|
|
12653
|
+
// Includes: App Router, shadcn/ui (Button, Card, Input), Tailwind, TypeScript
|
|
12654
|
+
\\\`\\\`\\\`
|
|
12655
|
+
|
|
12656
|
+
**What it creates:**
|
|
12657
|
+
- \\\`package.json\\\` with Next.js, React, Tailwind dependencies
|
|
12658
|
+
- \\\`tsconfig.json\\\` configured for Next.js
|
|
12659
|
+
- \\\`next.config.js\\\` with \\\`output: 'standalone'\\\` (required for deploy)
|
|
12660
|
+
- \\\`tailwind.config.ts\\\`, \\\`postcss.config.js\\\`
|
|
12661
|
+
- \\\`app/layout.tsx\\\`, \\\`app/page.tsx\\\`, \\\`app/globals.css\\\`
|
|
12662
|
+
- \\\`components/ui/\\\` with shadcn/ui components (Button, Card, Input)
|
|
12663
|
+
- \\\`lib/utils.ts\\\` with \\\`cn()\\\` utility
|
|
12664
|
+
|
|
12665
|
+
**Auto-runs:** \\\`npm install\\\` after creating files
|
|
12666
|
+
|
|
12667
|
+
---
|
|
12668
|
+
|
|
12669
|
+
### Tool 2: \\\`deploy_app\\\`
|
|
12670
|
+
|
|
12671
|
+
**Purpose:** Zip and deploy a Next.js project to Severino for hosting.
|
|
12672
|
+
|
|
12673
|
+
**Args:**
|
|
12674
|
+
- \\\`projectDir\\\` (required): Path to the Next.js project
|
|
12675
|
+
- \\\`name\\\` (optional): App name (default: package.json name or directory name)
|
|
12676
|
+
- \\\`severinoUrl\\\` (optional): Auto-reads from \\\`SEVERINO_URL\\\` env var
|
|
12677
|
+
- \\\`apiKey\\\` (optional): Auto-reads from \\\`SEVERINO_API_KEY\\\` env var
|
|
12678
|
+
|
|
12679
|
+
**Example:**
|
|
12680
|
+
\\\`\\\`\\\`typescript
|
|
12681
|
+
const result = await deployApp({
|
|
12682
|
+
projectDir: './erp-dashboard',
|
|
12683
|
+
name: 'erp-dashboard',
|
|
12684
|
+
});
|
|
12685
|
+
// Returns: { appId: 'uuid', url: 'http://localhost:3000/app/uuid', status: 'building' }
|
|
12686
|
+
\\\`\\\`\\\`
|
|
12687
|
+
|
|
12688
|
+
**What it does:**
|
|
12689
|
+
1. Validates project (checks \\\`package.json\\\` and \\\`next\\\` dependency)
|
|
12690
|
+
2. Creates ZIP (excludes \\\`node_modules\\\`, \\\`.next\\\`, \\\`.env\\\`, \\\`.git\\\`)
|
|
12691
|
+
3. Uploads to \\\`POST {severinoUrl}/api/v1/deploy\\\`
|
|
12692
|
+
4. Returns \\\`appId\\\` and live URL
|
|
12693
|
+
|
|
12694
|
+
**Important:** The deploy is **asynchronous** - status starts as \\\`'building'\\\`. The app will be ready in ~30-60 seconds.
|
|
12695
|
+
|
|
12696
|
+
---
|
|
12697
|
+
|
|
12698
|
+
### Complete Workflow Example
|
|
12699
|
+
|
|
12700
|
+
\\\`\\\`\\\`typescript
|
|
12701
|
+
// Step 1: Create project
|
|
12702
|
+
message({ message_type: 'info', content: 'Step 1/3: Creating Next.js project...' });
|
|
12703
|
+
const scaffold = await createNextApp({
|
|
12704
|
+
name: 'erp-dashboard',
|
|
12705
|
+
template: 'full',
|
|
12706
|
+
});
|
|
12707
|
+
|
|
12708
|
+
// Step 2: Develop (customize the app)
|
|
12709
|
+
message({ message_type: 'info', content: 'Step 2/3: Developing components...' });
|
|
12710
|
+
await fileWrite({
|
|
12711
|
+
filepath: './erp-dashboard/app/dashboard/page.tsx',
|
|
12712
|
+
content: 'export default function Dashboard() { return <h1>ERP Dashboard</h1> }',
|
|
12713
|
+
});
|
|
12714
|
+
|
|
12715
|
+
// Step 3: Deploy
|
|
12716
|
+
message({ message_type: 'info', content: 'Step 3/3: Deploying to Severino...' });
|
|
12717
|
+
const deploy = await deployApp({
|
|
12718
|
+
projectDir: './erp-dashboard',
|
|
12719
|
+
name: 'erp-dashboard',
|
|
12720
|
+
});
|
|
12721
|
+
|
|
12722
|
+
// Step 4: Report result
|
|
12723
|
+
message({
|
|
12724
|
+
message_type: 'result',
|
|
12725
|
+
content: 'App criada e deployada com sucesso!',
|
|
12726
|
+
attachments: [{ path: deploy.url, description: 'URL da app live' }],
|
|
12727
|
+
});
|
|
12728
|
+
\\\`\\\`\\\`
|
|
12729
|
+
|
|
12730
|
+
---
|
|
12731
|
+
|
|
12732
|
+
### \u26A0\uFE0F Critical Requirements for Deploy
|
|
12733
|
+
|
|
12734
|
+
1. **\\\`output: 'standalone'\\\` in \\\`next.config.js\\\`**
|
|
12735
|
+
- The \\\`create_next_app\\\` tool already includes this
|
|
12736
|
+
- If you modify \\\`next.config.js\\\`, keep this setting
|
|
12737
|
+
|
|
12738
|
+
2. **Build before deploy (optional but recommended)**
|
|
12739
|
+
\\\`\\\`\\\`bash
|
|
12740
|
+
cd erp-dashboard && npm run build
|
|
12741
|
+
\\\`\\\`\\\`
|
|
12742
|
+
- Ensures no TypeScript errors
|
|
12743
|
+
- Creates \\\`.next/standalone/\\\` for optimized deploy
|
|
12744
|
+
|
|
12745
|
+
3. **Don't include sensitive files**
|
|
12746
|
+
- The tool automatically excludes \\\`.env\\\`, \\\`.env.local\\\`, \\\`node_modules\\\`
|
|
12747
|
+
- Never commit API keys or secrets to the project
|
|
12748
|
+
|
|
12749
|
+
4. **Test locally first (if possible)**
|
|
12750
|
+
\\\`\\\`\\\`bash
|
|
12751
|
+
cd erp-dashboard && npm run dev
|
|
12752
|
+
\\\`\\\`\\\`
|
|
12753
|
+
|
|
12754
|
+
---
|
|
12755
|
+
|
|
12756
|
+
### Troubleshooting
|
|
12757
|
+
|
|
12758
|
+
**Deploy fails with "ZIP too large":**
|
|
12759
|
+
- Remove large files (videos, images > 5MB)
|
|
12760
|
+
- Exclude \\\`coverage/\\\`, \\\`.turbo/\\\` directories
|
|
12761
|
+
|
|
12762
|
+
**Deploy fails with "next not found":**
|
|
12763
|
+
- Ensure \\\`next\\\` is in \\\`dependencies\\\` or \\\`devDependencies\\\`
|
|
12764
|
+
- Run \\\`npm install\\\` before deploy
|
|
12765
|
+
|
|
12766
|
+
**App returns 404 after deploy:**
|
|
12767
|
+
- Wait 30-60 seconds for build to complete
|
|
12768
|
+
- Check status: \\\`GET {severinoUrl}/api/v1/apps/:appId\\\`
|
|
12769
|
+
|
|
12770
|
+
**Build fails:**
|
|
12771
|
+
- Run \\\`npx tsc --noEmit\\\` to check TypeScript errors
|
|
12772
|
+
- Fix errors before deploy
|
|
12773
|
+
|
|
12774
|
+
---
|
|
12775
|
+
|
|
11565
12776
|
## \u{1F3AF} QUALITY STANDARDS - PRODUCTION GRADE
|
|
11566
12777
|
|
|
11567
12778
|
### Code Quality
|
|
@@ -11791,10 +13002,10 @@ function getGitBranch(dir) {
|
|
|
11791
13002
|
}
|
|
11792
13003
|
function getPackageManager(dir) {
|
|
11793
13004
|
try {
|
|
11794
|
-
if (
|
|
11795
|
-
if (
|
|
11796
|
-
if (
|
|
11797
|
-
if (
|
|
13005
|
+
if (fs32.existsSync(path34.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
13006
|
+
if (fs32.existsSync(path34.join(dir, "yarn.lock"))) return "yarn";
|
|
13007
|
+
if (fs32.existsSync(path34.join(dir, "bun.lockb"))) return "bun";
|
|
13008
|
+
if (fs32.existsSync(path34.join(dir, "package-lock.json"))) return "npm";
|
|
11798
13009
|
return "unknown";
|
|
11799
13010
|
} catch {
|
|
11800
13011
|
return "unknown";
|
|
@@ -11802,9 +13013,9 @@ function getPackageManager(dir) {
|
|
|
11802
13013
|
}
|
|
11803
13014
|
function getProjectType(dir) {
|
|
11804
13015
|
try {
|
|
11805
|
-
const files =
|
|
13016
|
+
const files = fs32.readdirSync(dir);
|
|
11806
13017
|
if (files.includes("package.json")) {
|
|
11807
|
-
const pkg = JSON.parse(
|
|
13018
|
+
const pkg = JSON.parse(fs32.readFileSync(path34.join(dir, "package.json"), "utf-8"));
|
|
11808
13019
|
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
11809
13020
|
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
11810
13021
|
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
@@ -11823,9 +13034,9 @@ function getProjectType(dir) {
|
|
|
11823
13034
|
}
|
|
11824
13035
|
function getTestFramework(dir) {
|
|
11825
13036
|
try {
|
|
11826
|
-
const pkgPath =
|
|
11827
|
-
if (
|
|
11828
|
-
const pkg = JSON.parse(
|
|
13037
|
+
const pkgPath = path34.join(dir, "package.json");
|
|
13038
|
+
if (fs32.existsSync(pkgPath)) {
|
|
13039
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
11829
13040
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
11830
13041
|
if (deps.jest) return "jest";
|
|
11831
13042
|
if (deps.vitest) return "vitest";
|
|
@@ -11834,7 +13045,7 @@ function getTestFramework(dir) {
|
|
|
11834
13045
|
if (deps["@playwright/test"]) return "playwright";
|
|
11835
13046
|
if (deps.cypress) return "cypress";
|
|
11836
13047
|
}
|
|
11837
|
-
if (
|
|
13048
|
+
if (fs32.existsSync(path34.join(dir, "pytest.ini")) || fs32.existsSync(path34.join(dir, "conftest.py"))) return "pytest";
|
|
11838
13049
|
return "unknown";
|
|
11839
13050
|
} catch {
|
|
11840
13051
|
return "unknown";
|
|
@@ -11842,9 +13053,9 @@ function getTestFramework(dir) {
|
|
|
11842
13053
|
}
|
|
11843
13054
|
function getTestCommand(dir) {
|
|
11844
13055
|
try {
|
|
11845
|
-
const pkgPath =
|
|
11846
|
-
if (
|
|
11847
|
-
const pkg = JSON.parse(
|
|
13056
|
+
const pkgPath = path34.join(dir, "package.json");
|
|
13057
|
+
if (fs32.existsSync(pkgPath)) {
|
|
13058
|
+
const pkg = JSON.parse(fs32.readFileSync(pkgPath, "utf-8"));
|
|
11848
13059
|
if (pkg.scripts?.test) return `npm test`;
|
|
11849
13060
|
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
11850
13061
|
}
|
|
@@ -12005,12 +13216,12 @@ function getUnifiedSystemPrompt(availableSkills) {
|
|
|
12005
13216
|
const runtimeConfig = getRuntimeConfig();
|
|
12006
13217
|
const availablePlugins = listPlugins();
|
|
12007
13218
|
const env = {
|
|
12008
|
-
os_type:
|
|
12009
|
-
os_version:
|
|
12010
|
-
architecture:
|
|
13219
|
+
os_type: os21.type(),
|
|
13220
|
+
os_version: os21.release(),
|
|
13221
|
+
architecture: os21.arch(),
|
|
12011
13222
|
workdir: cwd,
|
|
12012
13223
|
shell_type: process.env.SHELL || process.env.COMSPEC || "unknown",
|
|
12013
|
-
username:
|
|
13224
|
+
username: os21.userInfo().username,
|
|
12014
13225
|
current_date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
12015
13226
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
12016
13227
|
is_git_repo: isGitRepo(cwd) ? "yes" : "no",
|
|
@@ -12081,8 +13292,8 @@ ${blumaMdContent}
|
|
|
12081
13292
|
}
|
|
12082
13293
|
function isGitRepo(dir) {
|
|
12083
13294
|
try {
|
|
12084
|
-
const gitPath =
|
|
12085
|
-
return
|
|
13295
|
+
const gitPath = path34.join(dir, ".git");
|
|
13296
|
+
return fs32.existsSync(gitPath) && fs32.lstatSync(gitPath).isDirectory();
|
|
12086
13297
|
} catch {
|
|
12087
13298
|
return false;
|
|
12088
13299
|
}
|
|
@@ -12245,8 +13456,9 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
12245
13456
|
const thresholdTokens = tokenBudget * compressThreshold;
|
|
12246
13457
|
let pendingSlices = turnSlices.slice(sliceCount, recentStart);
|
|
12247
13458
|
let pendingFlat = pendingSlices.flat();
|
|
13459
|
+
clearTokenCountCache();
|
|
12248
13460
|
let messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
|
|
12249
|
-
let tokens = countTokens(messages);
|
|
13461
|
+
let tokens = countTokens(messages, true);
|
|
12250
13462
|
while (tokens >= thresholdTokens && pendingSlices.length > 0) {
|
|
12251
13463
|
try {
|
|
12252
13464
|
anchor = await compressToAnchor(
|
|
@@ -12264,8 +13476,9 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
12264
13476
|
pendingSlices = [];
|
|
12265
13477
|
pendingFlat = [];
|
|
12266
13478
|
}
|
|
13479
|
+
clearTokenCountCache();
|
|
12267
13480
|
messages = buildContextMessages(systemMessages, anchor, pendingFlat, recentFlat);
|
|
12268
|
-
tokens = countTokens(messages);
|
|
13481
|
+
tokens = countTokens(messages, true);
|
|
12269
13482
|
}
|
|
12270
13483
|
return {
|
|
12271
13484
|
messages,
|
|
@@ -12276,7 +13489,7 @@ async function createApiContextWindow(fullHistory, currentAnchor, compressedTurn
|
|
|
12276
13489
|
|
|
12277
13490
|
// src/app/agent/core/llm/llm.ts
|
|
12278
13491
|
init_runtime_config();
|
|
12279
|
-
import
|
|
13492
|
+
import os22 from "os";
|
|
12280
13493
|
import OpenAI from "openai";
|
|
12281
13494
|
function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
12282
13495
|
const msg = String(userMessage || "").slice(0, 300);
|
|
@@ -12293,7 +13506,7 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
12293
13506
|
}
|
|
12294
13507
|
function getPreferredMacAddress() {
|
|
12295
13508
|
try {
|
|
12296
|
-
const ifaces =
|
|
13509
|
+
const ifaces = os22.networkInterfaces();
|
|
12297
13510
|
for (const name of Object.keys(ifaces)) {
|
|
12298
13511
|
const addrs = ifaces[name];
|
|
12299
13512
|
if (!addrs) continue;
|
|
@@ -12308,7 +13521,7 @@ function getPreferredMacAddress() {
|
|
|
12308
13521
|
} catch {
|
|
12309
13522
|
}
|
|
12310
13523
|
try {
|
|
12311
|
-
return `host:${
|
|
13524
|
+
return `host:${os22.hostname()}`;
|
|
12312
13525
|
} catch {
|
|
12313
13526
|
return "unknown";
|
|
12314
13527
|
}
|
|
@@ -12318,7 +13531,7 @@ function defaultInteractiveCliUserContextInput(sessionId, userMessage) {
|
|
|
12318
13531
|
const machineId = getPreferredMacAddress();
|
|
12319
13532
|
let userName = null;
|
|
12320
13533
|
try {
|
|
12321
|
-
userName =
|
|
13534
|
+
userName = os22.userInfo().username || null;
|
|
12322
13535
|
} catch {
|
|
12323
13536
|
userName = null;
|
|
12324
13537
|
}
|
|
@@ -12788,8 +14001,8 @@ function classifyToolInvocation(input) {
|
|
|
12788
14001
|
|
|
12789
14002
|
// src/app/agent/runtime/hook_registry.ts
|
|
12790
14003
|
init_sandbox_policy();
|
|
12791
|
-
import
|
|
12792
|
-
import
|
|
14004
|
+
import fs33 from "fs";
|
|
14005
|
+
import path35 from "path";
|
|
12793
14006
|
var DEFAULT_STATE = {
|
|
12794
14007
|
enabled: true,
|
|
12795
14008
|
maxEvents: 120,
|
|
@@ -12800,7 +14013,7 @@ var cache2 = null;
|
|
|
12800
14013
|
var cachePath2 = null;
|
|
12801
14014
|
function getStatePath() {
|
|
12802
14015
|
const policy = getSandboxPolicy();
|
|
12803
|
-
return
|
|
14016
|
+
return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
12804
14017
|
}
|
|
12805
14018
|
function getHookStatePath() {
|
|
12806
14019
|
return getStatePath();
|
|
@@ -12819,8 +14032,8 @@ function ensureLoaded2() {
|
|
|
12819
14032
|
return cache2;
|
|
12820
14033
|
}
|
|
12821
14034
|
try {
|
|
12822
|
-
if (
|
|
12823
|
-
const parsed = JSON.parse(
|
|
14035
|
+
if (fs33.existsSync(statePath)) {
|
|
14036
|
+
const parsed = JSON.parse(fs33.readFileSync(statePath, "utf-8"));
|
|
12824
14037
|
cache2 = {
|
|
12825
14038
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
12826
14039
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -12846,9 +14059,9 @@ function ensureLoaded2() {
|
|
|
12846
14059
|
}
|
|
12847
14060
|
function persist2(state) {
|
|
12848
14061
|
const statePath = getStatePath();
|
|
12849
|
-
|
|
14062
|
+
fs33.mkdirSync(path35.dirname(statePath), { recursive: true });
|
|
12850
14063
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
12851
|
-
|
|
14064
|
+
fs33.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
12852
14065
|
cache2 = state;
|
|
12853
14066
|
cachePath2 = statePath;
|
|
12854
14067
|
}
|
|
@@ -12945,11 +14158,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
12945
14158
|
}
|
|
12946
14159
|
|
|
12947
14160
|
// src/app/agent/tools/natives/coding_memory_consolidate.ts
|
|
12948
|
-
import * as
|
|
12949
|
-
import * as
|
|
12950
|
-
import
|
|
14161
|
+
import * as fs34 from "fs";
|
|
14162
|
+
import * as path36 from "path";
|
|
14163
|
+
import os23 from "os";
|
|
12951
14164
|
function memoryPath2() {
|
|
12952
|
-
return
|
|
14165
|
+
return path36.join(process.env.HOME || os23.homedir(), ".bluma", "coding_memory.json");
|
|
12953
14166
|
}
|
|
12954
14167
|
function normalizeNote2(note) {
|
|
12955
14168
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -12959,18 +14172,18 @@ function uniqTags(a, b) {
|
|
|
12959
14172
|
}
|
|
12960
14173
|
function consolidateCodingMemoryFile() {
|
|
12961
14174
|
const p = memoryPath2();
|
|
12962
|
-
if (!
|
|
14175
|
+
if (!fs34.existsSync(p)) {
|
|
12963
14176
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
12964
14177
|
}
|
|
12965
14178
|
const bak = `${p}.bak`;
|
|
12966
14179
|
try {
|
|
12967
|
-
|
|
14180
|
+
fs34.copyFileSync(p, bak);
|
|
12968
14181
|
} catch (e) {
|
|
12969
14182
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
12970
14183
|
}
|
|
12971
14184
|
let data;
|
|
12972
14185
|
try {
|
|
12973
|
-
data = JSON.parse(
|
|
14186
|
+
data = JSON.parse(fs34.readFileSync(p, "utf-8"));
|
|
12974
14187
|
} catch (e) {
|
|
12975
14188
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
12976
14189
|
}
|
|
@@ -13005,7 +14218,7 @@ function consolidateCodingMemoryFile() {
|
|
|
13005
14218
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13006
14219
|
};
|
|
13007
14220
|
try {
|
|
13008
|
-
|
|
14221
|
+
fs34.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
13009
14222
|
} catch (e) {
|
|
13010
14223
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
13011
14224
|
}
|
|
@@ -13603,7 +14816,7 @@ var BluMaAgent = class {
|
|
|
13603
14816
|
|
|
13604
14817
|
${editData.error.display}`;
|
|
13605
14818
|
}
|
|
13606
|
-
const filename =
|
|
14819
|
+
const filename = path37.basename(toolArgs.file_path);
|
|
13607
14820
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
13608
14821
|
} catch (e) {
|
|
13609
14822
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -13694,6 +14907,7 @@ ${editData.error.display}`;
|
|
|
13694
14907
|
});
|
|
13695
14908
|
await this.notifyFactorTurnEndIfNeeded("empty_reply_exhausted");
|
|
13696
14909
|
this.eventBus.emit("backend_message", { type: "done", status: "failed" });
|
|
14910
|
+
process.exit(1);
|
|
13697
14911
|
return;
|
|
13698
14912
|
}
|
|
13699
14913
|
await this._continueConversation();
|
|
@@ -13827,6 +15041,7 @@ ${editData.error.display}`;
|
|
|
13827
15041
|
});
|
|
13828
15042
|
await this.notifyFactorTurnEndIfNeeded("protocol_direct_text_exhausted");
|
|
13829
15043
|
this.emitTurnCompleted();
|
|
15044
|
+
process.exit(1);
|
|
13830
15045
|
return;
|
|
13831
15046
|
}
|
|
13832
15047
|
const feedback = this.feedbackSystem.generateFeedback({
|
|
@@ -13914,6 +15129,7 @@ ${editData.error.display}`;
|
|
|
13914
15129
|
});
|
|
13915
15130
|
await this.notifyFactorTurnEndIfNeeded("protocol_direct_text_exhausted");
|
|
13916
15131
|
this.emitTurnCompleted();
|
|
15132
|
+
process.exit(1);
|
|
13917
15133
|
return;
|
|
13918
15134
|
}
|
|
13919
15135
|
const feedback = this.feedbackSystem.generateFeedback({
|
|
@@ -13989,7 +15205,7 @@ import { v4 as uuidv411 } from "uuid";
|
|
|
13989
15205
|
import { v4 as uuidv410 } from "uuid";
|
|
13990
15206
|
|
|
13991
15207
|
// src/app/agent/subagents/init/init_system_prompt.ts
|
|
13992
|
-
import
|
|
15208
|
+
import os24 from "os";
|
|
13993
15209
|
var SYSTEM_PROMPT2 = `
|
|
13994
15210
|
|
|
13995
15211
|
### YOU ARE BluMa CLI \u2014 INIT SUBAGENT \u2014 AUTONOMOUS SENIOR SOFTWARE ENGINEER @ NOMADENGENUITY
|
|
@@ -14152,12 +15368,12 @@ Rule Summary:
|
|
|
14152
15368
|
function getInitPrompt() {
|
|
14153
15369
|
const now2 = /* @__PURE__ */ new Date();
|
|
14154
15370
|
const collectedData = {
|
|
14155
|
-
os_type:
|
|
14156
|
-
os_version:
|
|
14157
|
-
architecture:
|
|
15371
|
+
os_type: os24.type(),
|
|
15372
|
+
os_version: os24.release(),
|
|
15373
|
+
architecture: os24.arch(),
|
|
14158
15374
|
workdir: process.cwd(),
|
|
14159
15375
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
14160
|
-
username:
|
|
15376
|
+
username: os24.userInfo().username || "Unknown",
|
|
14161
15377
|
current_date: now2.toISOString().split("T")[0],
|
|
14162
15378
|
// Formato YYYY-MM-DD
|
|
14163
15379
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
@@ -14185,7 +15401,7 @@ function getInitPrompt() {
|
|
|
14185
15401
|
}
|
|
14186
15402
|
|
|
14187
15403
|
// src/app/agent/subagents/worker_system_prompt.ts
|
|
14188
|
-
import
|
|
15404
|
+
import os25 from "os";
|
|
14189
15405
|
var WORKER_SYSTEM_PROMPT = `
|
|
14190
15406
|
|
|
14191
15407
|
### YOU ARE BluMa CLI \u2014 WORKER AGENT \u2014 AUTONOMOUS SOFTWARE ENGINEERING SPECIALIST @ NOMADENGENUITY
|
|
@@ -14450,12 +15666,12 @@ You may be assigned different types of work:
|
|
|
14450
15666
|
function getWorkerPrompt() {
|
|
14451
15667
|
const now2 = /* @__PURE__ */ new Date();
|
|
14452
15668
|
const collectedData = {
|
|
14453
|
-
os_type:
|
|
14454
|
-
os_version:
|
|
14455
|
-
architecture:
|
|
15669
|
+
os_type: os25.type(),
|
|
15670
|
+
os_version: os25.release(),
|
|
15671
|
+
architecture: os25.arch(),
|
|
14456
15672
|
workdir: process.cwd(),
|
|
14457
15673
|
shell_type: process.env.SHELL || process.env.COMSPEC || "Unknown",
|
|
14458
|
-
username:
|
|
15674
|
+
username: os25.userInfo().username || "Unknown",
|
|
14459
15675
|
current_date: now2.toISOString().split("T")[0],
|
|
14460
15676
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone || "Unknown",
|
|
14461
15677
|
locale: process.env.LANG || process.env.LC_ALL || "Unknown"
|
|
@@ -14573,16 +15789,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
14573
15789
|
}
|
|
14574
15790
|
|
|
14575
15791
|
// src/app/agent/core/memory/session_memory.ts
|
|
14576
|
-
import
|
|
14577
|
-
import
|
|
14578
|
-
import
|
|
15792
|
+
import fs35 from "fs";
|
|
15793
|
+
import os26 from "os";
|
|
15794
|
+
import path38 from "path";
|
|
14579
15795
|
import { v4 as uuidv49 } from "uuid";
|
|
14580
15796
|
var SessionMemoryExtractor = class {
|
|
14581
15797
|
llmClient;
|
|
14582
15798
|
memoryFile;
|
|
14583
15799
|
constructor(options = {}) {
|
|
14584
15800
|
this.llmClient = options.llmClient;
|
|
14585
|
-
this.memoryFile = options.memoryFile ||
|
|
15801
|
+
this.memoryFile = options.memoryFile || path38.join(os26.homedir(), ".bluma", "session_memory.json");
|
|
14586
15802
|
}
|
|
14587
15803
|
/**
|
|
14588
15804
|
* Extract memories from conversation using LLM
|
|
@@ -14639,15 +15855,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
14639
15855
|
);
|
|
14640
15856
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
14641
15857
|
const trimmed = unique.slice(0, 200);
|
|
14642
|
-
|
|
15858
|
+
fs35.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
14643
15859
|
}
|
|
14644
15860
|
/**
|
|
14645
15861
|
* Load memories from disk
|
|
14646
15862
|
*/
|
|
14647
15863
|
async loadMemories() {
|
|
14648
15864
|
try {
|
|
14649
|
-
if (!
|
|
14650
|
-
const data =
|
|
15865
|
+
if (!fs35.existsSync(this.memoryFile)) return [];
|
|
15866
|
+
const data = fs35.readFileSync(this.memoryFile, "utf-8");
|
|
14651
15867
|
return JSON.parse(data);
|
|
14652
15868
|
} catch {
|
|
14653
15869
|
return [];
|
|
@@ -15184,14 +16400,14 @@ var RouteManager = class {
|
|
|
15184
16400
|
this.subAgents = subAgents;
|
|
15185
16401
|
this.core = core;
|
|
15186
16402
|
}
|
|
15187
|
-
registerRoute(
|
|
15188
|
-
this.routeHandlers.set(
|
|
16403
|
+
registerRoute(path43, handler) {
|
|
16404
|
+
this.routeHandlers.set(path43, handler);
|
|
15189
16405
|
}
|
|
15190
16406
|
async handleRoute(payload) {
|
|
15191
16407
|
const inputText = String(payload.content || "").trim();
|
|
15192
16408
|
const { userContext } = payload;
|
|
15193
|
-
for (const [
|
|
15194
|
-
if (inputText ===
|
|
16409
|
+
for (const [path43, handler] of this.routeHandlers) {
|
|
16410
|
+
if (inputText === path43 || inputText.startsWith(`${path43} `)) {
|
|
15195
16411
|
return handler({ content: inputText, userContext });
|
|
15196
16412
|
}
|
|
15197
16413
|
}
|
|
@@ -15200,13 +16416,13 @@ var RouteManager = class {
|
|
|
15200
16416
|
};
|
|
15201
16417
|
|
|
15202
16418
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
15203
|
-
import
|
|
16419
|
+
import path39 from "path";
|
|
15204
16420
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
15205
16421
|
async function loadPluginsAtStartup() {
|
|
15206
16422
|
for (const p of listPlugins()) {
|
|
15207
16423
|
const entry = p.manifest.entry?.trim();
|
|
15208
16424
|
if (!entry) continue;
|
|
15209
|
-
const abs =
|
|
16425
|
+
const abs = path39.resolve(p.root, entry);
|
|
15210
16426
|
try {
|
|
15211
16427
|
const href = pathToFileURL2(abs).href;
|
|
15212
16428
|
const mod = await import(href);
|
|
@@ -15227,7 +16443,7 @@ async function loadPluginsAtStartup() {
|
|
|
15227
16443
|
}
|
|
15228
16444
|
|
|
15229
16445
|
// src/app/agent/agent.ts
|
|
15230
|
-
var globalEnvPath =
|
|
16446
|
+
var globalEnvPath = path40.join(os27.homedir(), ".bluma", ".env");
|
|
15231
16447
|
dotenv.config({ path: globalEnvPath });
|
|
15232
16448
|
var Agent = class {
|
|
15233
16449
|
sessionId;
|
|
@@ -18467,16 +19683,16 @@ import latestVersion from "latest-version";
|
|
|
18467
19683
|
import semverGt from "semver/functions/gt.js";
|
|
18468
19684
|
import semverValid from "semver/functions/valid.js";
|
|
18469
19685
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
18470
|
-
import
|
|
18471
|
-
import
|
|
19686
|
+
import path41 from "path";
|
|
19687
|
+
import fs36 from "fs";
|
|
18472
19688
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
18473
19689
|
function findBlumaPackageJson(startDir) {
|
|
18474
19690
|
let dir = startDir;
|
|
18475
19691
|
for (let i = 0; i < 12; i++) {
|
|
18476
|
-
const candidate =
|
|
18477
|
-
if (
|
|
19692
|
+
const candidate = path41.join(dir, "package.json");
|
|
19693
|
+
if (fs36.existsSync(candidate)) {
|
|
18478
19694
|
try {
|
|
18479
|
-
const raw =
|
|
19695
|
+
const raw = fs36.readFileSync(candidate, "utf8");
|
|
18480
19696
|
const parsed = JSON.parse(raw);
|
|
18481
19697
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
18482
19698
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -18484,7 +19700,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
18484
19700
|
} catch {
|
|
18485
19701
|
}
|
|
18486
19702
|
}
|
|
18487
|
-
const parent =
|
|
19703
|
+
const parent = path41.dirname(dir);
|
|
18488
19704
|
if (parent === dir) break;
|
|
18489
19705
|
dir = parent;
|
|
18490
19706
|
}
|
|
@@ -18493,13 +19709,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
18493
19709
|
function resolveInstalledBlumaPackage() {
|
|
18494
19710
|
const tried = /* @__PURE__ */ new Set();
|
|
18495
19711
|
const tryFrom = (dir) => {
|
|
18496
|
-
const abs =
|
|
19712
|
+
const abs = path41.resolve(dir);
|
|
18497
19713
|
if (tried.has(abs)) return null;
|
|
18498
19714
|
tried.add(abs);
|
|
18499
19715
|
return findBlumaPackageJson(abs);
|
|
18500
19716
|
};
|
|
18501
19717
|
try {
|
|
18502
|
-
const fromBundle = tryFrom(
|
|
19718
|
+
const fromBundle = tryFrom(path41.dirname(fileURLToPath5(import.meta.url)));
|
|
18503
19719
|
if (fromBundle) return fromBundle;
|
|
18504
19720
|
} catch {
|
|
18505
19721
|
}
|
|
@@ -18507,12 +19723,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
18507
19723
|
if (argv1 && !argv1.startsWith("-")) {
|
|
18508
19724
|
try {
|
|
18509
19725
|
let resolved = argv1;
|
|
18510
|
-
if (
|
|
18511
|
-
resolved =
|
|
19726
|
+
if (path41.isAbsolute(argv1) && fs36.existsSync(argv1)) {
|
|
19727
|
+
resolved = fs36.realpathSync(argv1);
|
|
18512
19728
|
} else {
|
|
18513
|
-
resolved =
|
|
19729
|
+
resolved = path41.resolve(process.cwd(), argv1);
|
|
18514
19730
|
}
|
|
18515
|
-
const fromArgv = tryFrom(
|
|
19731
|
+
const fromArgv = tryFrom(path41.dirname(resolved));
|
|
18516
19732
|
if (fromArgv) return fromArgv;
|
|
18517
19733
|
} catch {
|
|
18518
19734
|
}
|
|
@@ -20052,9 +21268,9 @@ async function runAgentMode() {
|
|
|
20052
21268
|
try {
|
|
20053
21269
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
20054
21270
|
const filePath = args[inputFileIndex + 1];
|
|
20055
|
-
rawPayload =
|
|
21271
|
+
rawPayload = fs37.readFileSync(filePath, "utf-8");
|
|
20056
21272
|
} else {
|
|
20057
|
-
rawPayload =
|
|
21273
|
+
rawPayload = fs37.readFileSync(0, "utf-8");
|
|
20058
21274
|
}
|
|
20059
21275
|
} catch (err) {
|
|
20060
21276
|
writeAgentEvent(registrySessionId, {
|
|
@@ -20252,9 +21468,9 @@ async function runAgentMode() {
|
|
|
20252
21468
|
}
|
|
20253
21469
|
function readCliPackageVersion() {
|
|
20254
21470
|
try {
|
|
20255
|
-
const base =
|
|
20256
|
-
const pkgPath =
|
|
20257
|
-
const j = JSON.parse(
|
|
21471
|
+
const base = path42.dirname(fileURLToPath6(import.meta.url));
|
|
21472
|
+
const pkgPath = path42.join(base, "..", "package.json");
|
|
21473
|
+
const j = JSON.parse(fs37.readFileSync(pkgPath, "utf8"));
|
|
20258
21474
|
return String(j.version || "0.0.0");
|
|
20259
21475
|
} catch {
|
|
20260
21476
|
return "0.0.0";
|
|
@@ -20377,7 +21593,7 @@ function startBackgroundAgent() {
|
|
|
20377
21593
|
process.exit(1);
|
|
20378
21594
|
}
|
|
20379
21595
|
const filePath = args[inputFileIndex + 1];
|
|
20380
|
-
const rawPayload =
|
|
21596
|
+
const rawPayload = fs37.readFileSync(filePath, "utf-8");
|
|
20381
21597
|
const envelope = JSON.parse(rawPayload);
|
|
20382
21598
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
20383
21599
|
registerSession({
|
|
@@ -20394,7 +21610,7 @@ function startBackgroundAgent() {
|
|
|
20394
21610
|
}
|
|
20395
21611
|
});
|
|
20396
21612
|
const childArgs = [process.argv[1], "agent", "--input-file", filePath, "--background-worker", "--registry-session", sessionId];
|
|
20397
|
-
const child =
|
|
21613
|
+
const child = spawn6(process.execPath, childArgs, {
|
|
20398
21614
|
detached: true,
|
|
20399
21615
|
stdio: "ignore",
|
|
20400
21616
|
cwd: process.cwd(),
|