@nomad-e/bluma-cli 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/skills/factorai-sh/SKILL.md +5 -5
- package/dist/main.js +617 -273
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -3204,8 +3204,8 @@ __export(exportConversation_exports, {
|
|
|
3204
3204
|
formatConversationMarkdown: () => formatConversationMarkdown,
|
|
3205
3205
|
resolveExportOutputPath: () => resolveExportOutputPath
|
|
3206
3206
|
});
|
|
3207
|
-
import { promises as
|
|
3208
|
-
import
|
|
3207
|
+
import { promises as fs49 } from "fs";
|
|
3208
|
+
import path55 from "path";
|
|
3209
3209
|
function extractTextParts2(content) {
|
|
3210
3210
|
if (typeof content === "string") {
|
|
3211
3211
|
const trimmed = content.trim();
|
|
@@ -3363,12 +3363,12 @@ function resolveExportOutputPath(sessionId, options) {
|
|
|
3363
3363
|
const { outputPath, defaultExportDir } = options ?? {};
|
|
3364
3364
|
if (outputPath?.trim()) {
|
|
3365
3365
|
const raw = outputPath.trim();
|
|
3366
|
-
return
|
|
3366
|
+
return path55.isAbsolute(raw) ? raw : path55.resolve(process.cwd(), raw);
|
|
3367
3367
|
}
|
|
3368
3368
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
3369
3369
|
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 48) || "session";
|
|
3370
3370
|
const baseDir = defaultExportDir ?? getSandboxPolicy().workspaceRoot;
|
|
3371
|
-
return
|
|
3371
|
+
return path55.join(baseDir, `bluma-export-${safeId}-${stamp}.md`);
|
|
3372
3372
|
}
|
|
3373
3373
|
async function resolveHistory(sessionId, fallbackHistory) {
|
|
3374
3374
|
const loaded2 = await loadSession(sessionId);
|
|
@@ -3390,8 +3390,8 @@ async function exportConversationToFile(options) {
|
|
|
3390
3390
|
}
|
|
3391
3391
|
const markdown = formatConversationMarkdown(sessionId, history);
|
|
3392
3392
|
const filePath = resolveExportOutputPath(sessionId, { outputPath, defaultExportDir });
|
|
3393
|
-
await
|
|
3394
|
-
await
|
|
3393
|
+
await fs49.mkdir(path55.dirname(filePath), { recursive: true });
|
|
3394
|
+
await fs49.writeFile(filePath, markdown, "utf-8");
|
|
3395
3395
|
return {
|
|
3396
3396
|
success: true,
|
|
3397
3397
|
filePath,
|
|
@@ -3916,11 +3916,11 @@ var NodeFsOperations = {
|
|
|
3916
3916
|
if (fd) fs.closeSync(fd);
|
|
3917
3917
|
}
|
|
3918
3918
|
},
|
|
3919
|
-
appendFileSync(
|
|
3920
|
-
const _ = slowLogging`fs.appendFileSync(${
|
|
3919
|
+
appendFileSync(path60, data, options) {
|
|
3920
|
+
const _ = slowLogging`fs.appendFileSync(${path60}, ${data.length} chars)`;
|
|
3921
3921
|
if (options?.mode !== void 0) {
|
|
3922
3922
|
try {
|
|
3923
|
-
const fd = fs.openSync(
|
|
3923
|
+
const fd = fs.openSync(path60, "ax", options.mode);
|
|
3924
3924
|
try {
|
|
3925
3925
|
fs.appendFileSync(fd, data);
|
|
3926
3926
|
} finally {
|
|
@@ -3931,35 +3931,35 @@ var NodeFsOperations = {
|
|
|
3931
3931
|
if (getErrnoCode(e) !== "EEXIST") throw e;
|
|
3932
3932
|
}
|
|
3933
3933
|
}
|
|
3934
|
-
fs.appendFileSync(
|
|
3934
|
+
fs.appendFileSync(path60, data);
|
|
3935
3935
|
},
|
|
3936
3936
|
copyFileSync(src, dest) {
|
|
3937
3937
|
const _ = slowLogging`fs.copyFileSync(${src} → ${dest})`;
|
|
3938
3938
|
fs.copyFileSync(src, dest);
|
|
3939
3939
|
},
|
|
3940
|
-
unlinkSync(
|
|
3941
|
-
const _ = slowLogging`fs.unlinkSync(${
|
|
3942
|
-
fs.unlinkSync(
|
|
3940
|
+
unlinkSync(path60) {
|
|
3941
|
+
const _ = slowLogging`fs.unlinkSync(${path60})`;
|
|
3942
|
+
fs.unlinkSync(path60);
|
|
3943
3943
|
},
|
|
3944
3944
|
renameSync(oldPath, newPath) {
|
|
3945
3945
|
const _ = slowLogging`fs.renameSync(${oldPath} → ${newPath})`;
|
|
3946
3946
|
fs.renameSync(oldPath, newPath);
|
|
3947
3947
|
},
|
|
3948
|
-
linkSync(target,
|
|
3949
|
-
const _ = slowLogging`fs.linkSync(${target} → ${
|
|
3950
|
-
fs.linkSync(target,
|
|
3948
|
+
linkSync(target, path60) {
|
|
3949
|
+
const _ = slowLogging`fs.linkSync(${target} → ${path60})`;
|
|
3950
|
+
fs.linkSync(target, path60);
|
|
3951
3951
|
},
|
|
3952
|
-
symlinkSync(target,
|
|
3953
|
-
const _ = slowLogging`fs.symlinkSync(${target} → ${
|
|
3954
|
-
fs.symlinkSync(target,
|
|
3952
|
+
symlinkSync(target, path60, type) {
|
|
3953
|
+
const _ = slowLogging`fs.symlinkSync(${target} → ${path60})`;
|
|
3954
|
+
fs.symlinkSync(target, path60, type);
|
|
3955
3955
|
},
|
|
3956
|
-
readlinkSync(
|
|
3957
|
-
const _ = slowLogging`fs.readlinkSync(${
|
|
3958
|
-
return fs.readlinkSync(
|
|
3956
|
+
readlinkSync(path60) {
|
|
3957
|
+
const _ = slowLogging`fs.readlinkSync(${path60})`;
|
|
3958
|
+
return fs.readlinkSync(path60);
|
|
3959
3959
|
},
|
|
3960
|
-
realpathSync(
|
|
3961
|
-
const _ = slowLogging`fs.realpathSync(${
|
|
3962
|
-
return fs.realpathSync(
|
|
3960
|
+
realpathSync(path60) {
|
|
3961
|
+
const _ = slowLogging`fs.realpathSync(${path60})`;
|
|
3962
|
+
return fs.realpathSync(path60).normalize("NFC");
|
|
3963
3963
|
},
|
|
3964
3964
|
mkdirSync(dirPath, options) {
|
|
3965
3965
|
const _ = slowLogging`fs.mkdirSync(${dirPath})`;
|
|
@@ -3992,12 +3992,12 @@ var NodeFsOperations = {
|
|
|
3992
3992
|
const _ = slowLogging`fs.rmdirSync(${dirPath})`;
|
|
3993
3993
|
fs.rmdirSync(dirPath);
|
|
3994
3994
|
},
|
|
3995
|
-
rmSync(
|
|
3996
|
-
const _ = slowLogging`fs.rmSync(${
|
|
3997
|
-
fs.rmSync(
|
|
3995
|
+
rmSync(path60, options) {
|
|
3996
|
+
const _ = slowLogging`fs.rmSync(${path60})`;
|
|
3997
|
+
fs.rmSync(path60, options);
|
|
3998
3998
|
},
|
|
3999
|
-
createWriteStream(
|
|
4000
|
-
return fs.createWriteStream(
|
|
3999
|
+
createWriteStream(path60) {
|
|
4000
|
+
return fs.createWriteStream(path60);
|
|
4001
4001
|
},
|
|
4002
4002
|
async readFileBytes(fsPath, maxBytes) {
|
|
4003
4003
|
if (maxBytes === void 0) {
|
|
@@ -4104,12 +4104,12 @@ function shouldLogDebugMessage(message2) {
|
|
|
4104
4104
|
var hasFormattedOutput = false;
|
|
4105
4105
|
var debugWriter = null;
|
|
4106
4106
|
var pendingWrite = Promise.resolve();
|
|
4107
|
-
async function appendAsync(needMkdir, dir,
|
|
4107
|
+
async function appendAsync(needMkdir, dir, path60, content) {
|
|
4108
4108
|
if (needMkdir) {
|
|
4109
4109
|
await mkdir(dir, { recursive: true }).catch(() => {
|
|
4110
4110
|
});
|
|
4111
4111
|
}
|
|
4112
|
-
await appendFile(
|
|
4112
|
+
await appendFile(path60, content);
|
|
4113
4113
|
void updateLatestDebugLogSymlink();
|
|
4114
4114
|
}
|
|
4115
4115
|
function noop() {
|
|
@@ -4119,8 +4119,8 @@ function getDebugWriter() {
|
|
|
4119
4119
|
let ensuredDir = null;
|
|
4120
4120
|
debugWriter = createBufferedWriter({
|
|
4121
4121
|
writeFn: (content) => {
|
|
4122
|
-
const
|
|
4123
|
-
const dir = dirname(
|
|
4122
|
+
const path60 = getDebugLogPath();
|
|
4123
|
+
const dir = dirname(path60);
|
|
4124
4124
|
const needMkdir = ensuredDir !== dir;
|
|
4125
4125
|
ensuredDir = dir;
|
|
4126
4126
|
if (isDebugMode()) {
|
|
@@ -4130,11 +4130,11 @@ function getDebugWriter() {
|
|
|
4130
4130
|
} catch {
|
|
4131
4131
|
}
|
|
4132
4132
|
}
|
|
4133
|
-
getFsImplementation().appendFileSync(
|
|
4133
|
+
getFsImplementation().appendFileSync(path60, content);
|
|
4134
4134
|
void updateLatestDebugLogSymlink();
|
|
4135
4135
|
return;
|
|
4136
4136
|
}
|
|
4137
|
-
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir,
|
|
4137
|
+
pendingWrite = pendingWrite.then(appendAsync.bind(null, needMkdir, dir, path60, content)).catch(noop);
|
|
4138
4138
|
},
|
|
4139
4139
|
flushIntervalMs: 1e3,
|
|
4140
4140
|
maxBufferSize: 100,
|
|
@@ -10321,8 +10321,8 @@ import codeExcerpt from "code-excerpt";
|
|
|
10321
10321
|
import { readFileSync as readFileSync2 } from "fs";
|
|
10322
10322
|
import StackUtils from "stack-utils";
|
|
10323
10323
|
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
10324
|
-
var cleanupPath = (
|
|
10325
|
-
return
|
|
10324
|
+
var cleanupPath = (path60) => {
|
|
10325
|
+
return path60?.replace(`file://${process.cwd()}/`, "");
|
|
10326
10326
|
};
|
|
10327
10327
|
var stackUtils;
|
|
10328
10328
|
function getStackUtils() {
|
|
@@ -14299,8 +14299,8 @@ var getInstance = (stdout, createInstance) => {
|
|
|
14299
14299
|
|
|
14300
14300
|
// src/main.ts
|
|
14301
14301
|
import { EventEmitter as EventEmitter7 } from "events";
|
|
14302
|
-
import
|
|
14303
|
-
import
|
|
14302
|
+
import fs53 from "fs";
|
|
14303
|
+
import path59 from "path";
|
|
14304
14304
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
14305
14305
|
import { spawn as spawn6 } from "child_process";
|
|
14306
14306
|
import { v4 as uuidv412 } from "uuid";
|
|
@@ -14979,7 +14979,7 @@ function cancelSlashSubmenu() {
|
|
|
14979
14979
|
|
|
14980
14980
|
// src/app/agent/agent.ts
|
|
14981
14981
|
import * as dotenv from "dotenv";
|
|
14982
|
-
import
|
|
14982
|
+
import path49 from "path";
|
|
14983
14983
|
import os30 from "os";
|
|
14984
14984
|
|
|
14985
14985
|
// src/app/agent/tool_invoker.ts
|
|
@@ -20269,7 +20269,7 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
|
20269
20269
|
name: data.name,
|
|
20270
20270
|
status: data.status || "building",
|
|
20271
20271
|
url: appContext.appUrl || severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
|
|
20272
|
-
message: data.message || "Deploy iniciado",
|
|
20272
|
+
message: data.message || "Deploy iniciado. Chama factorai.sh.get_app_status({ appId, wait: true }) at\xE9 status ready ou failed; se failed, l\xEA typescriptErrors/buildLogTail e corrige o c\xF3digo.",
|
|
20273
20273
|
isRedeploy: !!appId,
|
|
20274
20274
|
appContext
|
|
20275
20275
|
};
|
|
@@ -20413,6 +20413,102 @@ async function deployApp(args) {
|
|
|
20413
20413
|
}
|
|
20414
20414
|
}
|
|
20415
20415
|
|
|
20416
|
+
// src/app/agent/runtime/factorai_deploy_feedback.ts
|
|
20417
|
+
var FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "build_failed"]);
|
|
20418
|
+
var READY_STATUSES = /* @__PURE__ */ new Set(["ready", "running", "live"]);
|
|
20419
|
+
var BUILDING_STATUSES = /* @__PURE__ */ new Set(["building", "deploying", "installing", "pending"]);
|
|
20420
|
+
function extractTypeScriptErrors(logText) {
|
|
20421
|
+
if (!logText.trim()) return [];
|
|
20422
|
+
const lines = logText.split("\n");
|
|
20423
|
+
const blocks = [];
|
|
20424
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20425
|
+
const line = lines[i];
|
|
20426
|
+
if (line.includes("Type error:") || line.includes("Failed to compile") || /^\s*>\s*\d+\s*\|/.test(line)) {
|
|
20427
|
+
const start = Math.max(0, i - 3);
|
|
20428
|
+
const end = Math.min(lines.length, i + 8);
|
|
20429
|
+
blocks.push(lines.slice(start, end).join("\n").trim());
|
|
20430
|
+
}
|
|
20431
|
+
}
|
|
20432
|
+
return [...new Set(blocks)].slice(0, 6);
|
|
20433
|
+
}
|
|
20434
|
+
function normalizeFactorAiAppStatus(raw, appId) {
|
|
20435
|
+
const envelope = raw && typeof raw === "object" ? raw : {};
|
|
20436
|
+
const data = envelope.data && typeof envelope.data === "object" && !Array.isArray(envelope.data) ? envelope.data : envelope;
|
|
20437
|
+
const status = String(data.status ?? "unknown").toLowerCase();
|
|
20438
|
+
const logs = typeof data.logs === "string" ? data.logs : "";
|
|
20439
|
+
const logTail = logs.length > 12e3 ? logs.slice(-12e3) : logs;
|
|
20440
|
+
const lastError = typeof data.lastError === "string" && data.lastError || typeof data.error === "string" && data.error || void 0;
|
|
20441
|
+
const slug = typeof data.slug === "string" ? data.slug : void 0;
|
|
20442
|
+
const url = typeof data.url === "string" ? data.url : void 0;
|
|
20443
|
+
const tsErrors = extractTypeScriptErrors(logTail);
|
|
20444
|
+
if (FAILED_STATUSES.has(status) || lastError) {
|
|
20445
|
+
const error = lastError || tsErrors[0] || (logTail.includes("next build falhou") ? logTail.split("\n").slice(-5).join("\n") : "") || "Deploy/build failed on FactorAI.sh";
|
|
20446
|
+
return {
|
|
20447
|
+
success: false,
|
|
20448
|
+
terminal: true,
|
|
20449
|
+
status: "failed",
|
|
20450
|
+
appId,
|
|
20451
|
+
slug,
|
|
20452
|
+
url,
|
|
20453
|
+
error,
|
|
20454
|
+
buildLogTail: logTail || void 0,
|
|
20455
|
+
typescriptErrors: tsErrors.length > 0 ? tsErrors : void 0,
|
|
20456
|
+
agentGuidance: "O build falhou no servidor. Corrige os erros TypeScript/compila\xE7\xE3o no projeto local (ex.: components/ui/card.tsx), confirma com npx tsc --noEmit se poss\xEDvel, depois factorai.sh.apply_app_changes ou deploy_app. N\xE3o digas ao utilizador que a app est\xE1 online."
|
|
20457
|
+
};
|
|
20458
|
+
}
|
|
20459
|
+
if (READY_STATUSES.has(status)) {
|
|
20460
|
+
return {
|
|
20461
|
+
success: true,
|
|
20462
|
+
terminal: true,
|
|
20463
|
+
status: "ready",
|
|
20464
|
+
appId,
|
|
20465
|
+
slug,
|
|
20466
|
+
url,
|
|
20467
|
+
buildLogTail: logTail || void 0
|
|
20468
|
+
};
|
|
20469
|
+
}
|
|
20470
|
+
if (BUILDING_STATUSES.has(status)) {
|
|
20471
|
+
return {
|
|
20472
|
+
success: true,
|
|
20473
|
+
terminal: false,
|
|
20474
|
+
status,
|
|
20475
|
+
appId,
|
|
20476
|
+
slug,
|
|
20477
|
+
url,
|
|
20478
|
+
buildLogTail: logTail || void 0
|
|
20479
|
+
};
|
|
20480
|
+
}
|
|
20481
|
+
return {
|
|
20482
|
+
success: true,
|
|
20483
|
+
terminal: false,
|
|
20484
|
+
status,
|
|
20485
|
+
appId,
|
|
20486
|
+
slug,
|
|
20487
|
+
url,
|
|
20488
|
+
buildLogTail: logTail || void 0
|
|
20489
|
+
};
|
|
20490
|
+
}
|
|
20491
|
+
function formatFactorAiDeployStatusForAgent(result) {
|
|
20492
|
+
const lines = [`status=${result.status}`, `appId=${result.appId}`];
|
|
20493
|
+
if (result.slug) lines.push(`slug=${result.slug}`);
|
|
20494
|
+
if (result.url) lines.push(`url=${result.url}`);
|
|
20495
|
+
if (result.error) lines.push(`error=${result.error}`);
|
|
20496
|
+
if (result.typescriptErrors?.length) {
|
|
20497
|
+
lines.push("typescriptErrors:");
|
|
20498
|
+
for (const block of result.typescriptErrors) {
|
|
20499
|
+
lines.push(block);
|
|
20500
|
+
}
|
|
20501
|
+
} else if (result.buildLogTail) {
|
|
20502
|
+
lines.push("buildLogTail:");
|
|
20503
|
+
lines.push(result.buildLogTail);
|
|
20504
|
+
}
|
|
20505
|
+
if (result.agentGuidance) lines.push(`guidance=${result.agentGuidance}`);
|
|
20506
|
+
return lines.join("\n");
|
|
20507
|
+
}
|
|
20508
|
+
function sleep(ms) {
|
|
20509
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
20510
|
+
}
|
|
20511
|
+
|
|
20416
20512
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
20417
20513
|
init_sandbox_policy();
|
|
20418
20514
|
function getFactorAiBaseUrl() {
|
|
@@ -20494,9 +20590,81 @@ async function resolveFactorAiAppId(explicitAppId) {
|
|
|
20494
20590
|
"appId em falta: passa o UUID de factorai.sh.json (appContext.appId) ou faz deploy_app primeiro nesta sess\xE3o."
|
|
20495
20591
|
);
|
|
20496
20592
|
}
|
|
20593
|
+
function isFactorAiNotFoundError(err) {
|
|
20594
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
20595
|
+
return /not found/i.test(msg) || /404/.test(msg);
|
|
20596
|
+
}
|
|
20597
|
+
async function fetchAndNormalizeAppStatus(appId) {
|
|
20598
|
+
const payload = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
|
|
20599
|
+
return normalizeFactorAiAppStatus(payload, appId);
|
|
20600
|
+
}
|
|
20497
20601
|
async function factorAiGetAppStatus(args) {
|
|
20498
20602
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
20499
|
-
|
|
20603
|
+
const wait = args?.wait === true;
|
|
20604
|
+
const timeoutMs = Math.max(5, args?.timeoutSeconds ?? 480) * 1e3;
|
|
20605
|
+
const pollMs = Math.max(2, args?.pollIntervalSeconds ?? 5) * 1e3;
|
|
20606
|
+
const deadline = Date.now() + timeoutMs;
|
|
20607
|
+
const finalize = (result) => ({
|
|
20608
|
+
success: true,
|
|
20609
|
+
data: {
|
|
20610
|
+
...result,
|
|
20611
|
+
summary: formatFactorAiDeployStatusForAgent(result)
|
|
20612
|
+
}
|
|
20613
|
+
});
|
|
20614
|
+
if (!wait) {
|
|
20615
|
+
try {
|
|
20616
|
+
return finalize(await fetchAndNormalizeAppStatus(appId));
|
|
20617
|
+
} catch (err) {
|
|
20618
|
+
if (isFactorAiNotFoundError(err)) {
|
|
20619
|
+
return finalize({
|
|
20620
|
+
success: false,
|
|
20621
|
+
terminal: true,
|
|
20622
|
+
status: "failed",
|
|
20623
|
+
appId,
|
|
20624
|
+
error: String(err instanceof Error ? err.message : err),
|
|
20625
|
+
agentGuidance: "App n\xE3o encontrada no FactorAI.sh \u2014 o deploy provavelmente falhou e o registo foi limpo, ou o appId est\xE1 errado. L\xEA factorai.sh.json, confirma FACTORAI_BASE_URL=SEVERINO_URL, e faz deploy de novo ap\xF3s corrigir o c\xF3digo."
|
|
20626
|
+
});
|
|
20627
|
+
}
|
|
20628
|
+
throw err;
|
|
20629
|
+
}
|
|
20630
|
+
}
|
|
20631
|
+
let sawBuilding = false;
|
|
20632
|
+
while (Date.now() < deadline) {
|
|
20633
|
+
try {
|
|
20634
|
+
const result = await fetchAndNormalizeAppStatus(appId);
|
|
20635
|
+
if (!result.terminal && result.status !== "failed") {
|
|
20636
|
+
sawBuilding = true;
|
|
20637
|
+
}
|
|
20638
|
+
if (result.terminal) {
|
|
20639
|
+
if (result.status === "ready" && !sawBuilding) {
|
|
20640
|
+
await sleep(pollMs);
|
|
20641
|
+
continue;
|
|
20642
|
+
}
|
|
20643
|
+
return finalize(result);
|
|
20644
|
+
}
|
|
20645
|
+
} catch (err) {
|
|
20646
|
+
if (isFactorAiNotFoundError(err)) {
|
|
20647
|
+
return finalize({
|
|
20648
|
+
success: false,
|
|
20649
|
+
terminal: true,
|
|
20650
|
+
status: "failed",
|
|
20651
|
+
appId,
|
|
20652
|
+
error: String(err instanceof Error ? err.message : err),
|
|
20653
|
+
agentGuidance: "Deploy falhou: a app desapareceu do registry (build error no servidor). Corrige o c\xF3digo e volta a deploy_app."
|
|
20654
|
+
});
|
|
20655
|
+
}
|
|
20656
|
+
throw err;
|
|
20657
|
+
}
|
|
20658
|
+
await sleep(pollMs);
|
|
20659
|
+
}
|
|
20660
|
+
return finalize({
|
|
20661
|
+
success: true,
|
|
20662
|
+
terminal: false,
|
|
20663
|
+
status: "building",
|
|
20664
|
+
appId,
|
|
20665
|
+
error: `Timeout ap\xF3s ${Math.round(timeoutMs / 1e3)}s \xE0 espera de ready/failed`,
|
|
20666
|
+
agentGuidance: "Continua a pollar get_app_status ou verifica logs no FactorAI.sh."
|
|
20667
|
+
});
|
|
20500
20668
|
}
|
|
20501
20669
|
async function factorAiApplyAppChanges(args) {
|
|
20502
20670
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
@@ -20512,7 +20680,13 @@ async function factorAiApplyAppChanges(args) {
|
|
|
20512
20680
|
})
|
|
20513
20681
|
});
|
|
20514
20682
|
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
20515
|
-
|
|
20683
|
+
const normalized = normalizeFactorAiAppStatus(response, appId);
|
|
20684
|
+
const summary = formatFactorAiDeployStatusForAgent(normalized);
|
|
20685
|
+
const message2 = args?.deploy !== false && !normalized.terminal ? "Changes accepted; build em curso. Chama factorai.sh.get_app_status({ wait: true }) at\xE9 ready ou failed." : normalized.success ? "Changes applied" : normalized.error;
|
|
20686
|
+
return {
|
|
20687
|
+
success: true,
|
|
20688
|
+
data: { ...normalized, summary, message: message2 }
|
|
20689
|
+
};
|
|
20516
20690
|
}
|
|
20517
20691
|
async function factorAiRedeployApp(args) {
|
|
20518
20692
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
@@ -20520,7 +20694,13 @@ async function factorAiRedeployApp(args) {
|
|
|
20520
20694
|
method: "POST"
|
|
20521
20695
|
});
|
|
20522
20696
|
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
20523
|
-
|
|
20697
|
+
const normalized = normalizeFactorAiAppStatus(response, appId);
|
|
20698
|
+
const summary = formatFactorAiDeployStatusForAgent(normalized);
|
|
20699
|
+
const message2 = normalized.terminal ? normalized.success ? "Redeploy complete" : normalized.error : "Redeploy started; poll get_app_status({ wait: true }).";
|
|
20700
|
+
return {
|
|
20701
|
+
success: true,
|
|
20702
|
+
data: { ...normalized, summary, message: message2 }
|
|
20703
|
+
};
|
|
20524
20704
|
}
|
|
20525
20705
|
function extractFactorAiResponseData(response) {
|
|
20526
20706
|
if (response && typeof response === "object" && response.data && typeof response.data === "object") {
|
|
@@ -20607,13 +20787,25 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
20607
20787
|
type: "function",
|
|
20608
20788
|
function: {
|
|
20609
20789
|
name: "factorai.sh.get_app_status",
|
|
20610
|
-
description: "Poll deploy/rebuild status
|
|
20790
|
+
description: "Poll deploy/rebuild status. On failed returns buildLogTail + typescriptErrors so you can fix code. Use wait:true after deploy_app/apply_app_changes until ready or failed before message(result).",
|
|
20611
20791
|
parameters: {
|
|
20612
20792
|
type: "object",
|
|
20613
20793
|
properties: {
|
|
20614
20794
|
appId: {
|
|
20615
20795
|
type: "string",
|
|
20616
20796
|
description: "UUID from factorai.sh.json (appContext.appId). Optional if manifest exists in cwd. Do not use URL slug."
|
|
20797
|
+
},
|
|
20798
|
+
wait: {
|
|
20799
|
+
type: "boolean",
|
|
20800
|
+
description: "If true, poll until ready/failed/timeout (recommended after deploy)."
|
|
20801
|
+
},
|
|
20802
|
+
timeoutSeconds: {
|
|
20803
|
+
type: "number",
|
|
20804
|
+
description: "Max wait time when wait=true (default 480)."
|
|
20805
|
+
},
|
|
20806
|
+
pollIntervalSeconds: {
|
|
20807
|
+
type: "number",
|
|
20808
|
+
description: "Seconds between polls when wait=true (default 5)."
|
|
20617
20809
|
}
|
|
20618
20810
|
},
|
|
20619
20811
|
required: [],
|
|
@@ -21836,14 +22028,14 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
21836
22028
|
|
|
21837
22029
|
// src/app/agent/bluma/core/bluma.ts
|
|
21838
22030
|
init_session_manager();
|
|
21839
|
-
import
|
|
21840
|
-
import
|
|
22031
|
+
import path46 from "path";
|
|
22032
|
+
import fs41 from "fs";
|
|
21841
22033
|
import { v4 as uuidv48 } from "uuid";
|
|
21842
22034
|
|
|
21843
22035
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
21844
22036
|
import os23 from "os";
|
|
21845
|
-
import
|
|
21846
|
-
import
|
|
22037
|
+
import fs37 from "fs";
|
|
22038
|
+
import path40 from "path";
|
|
21847
22039
|
import { execSync as execSync3 } from "child_process";
|
|
21848
22040
|
|
|
21849
22041
|
// src/app/agent/skills/skill_loader.ts
|
|
@@ -23700,12 +23892,12 @@ init_session_memory_paths();
|
|
|
23700
23892
|
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
23701
23893
|
async function loadSessionMemoryForPrompt(sessionId, cwd2 = process.cwd()) {
|
|
23702
23894
|
if (!sessionId?.trim()) return "";
|
|
23703
|
-
const
|
|
23895
|
+
const path60 = getSessionMemoryPath(sessionId, cwd2);
|
|
23704
23896
|
try {
|
|
23705
|
-
const content = await readFile3(
|
|
23897
|
+
const content = await readFile3(path60, "utf-8");
|
|
23706
23898
|
if (!content.trim()) return "";
|
|
23707
23899
|
return `<session_memory>
|
|
23708
|
-
Notes for this conversation (${
|
|
23900
|
+
Notes for this conversation (${path60}):
|
|
23709
23901
|
|
|
23710
23902
|
${content.trim()}
|
|
23711
23903
|
</session_memory>`;
|
|
@@ -23715,11 +23907,11 @@ ${content.trim()}
|
|
|
23715
23907
|
}
|
|
23716
23908
|
async function initSessionMemoryFile(sessionId, cwd2 = process.cwd()) {
|
|
23717
23909
|
await ensureSessionMemoryDir(sessionId, cwd2);
|
|
23718
|
-
const
|
|
23910
|
+
const path60 = getSessionMemoryPath(sessionId, cwd2);
|
|
23719
23911
|
try {
|
|
23720
|
-
await readFile3(
|
|
23912
|
+
await readFile3(path60, "utf-8");
|
|
23721
23913
|
} catch {
|
|
23722
|
-
await writeFile2(
|
|
23914
|
+
await writeFile2(path60, `${DEFAULT_SESSION_MEMORY_TEMPLATE}
|
|
23723
23915
|
`, "utf-8");
|
|
23724
23916
|
}
|
|
23725
23917
|
}
|
|
@@ -23731,14 +23923,14 @@ var FACTORAI_SH_SECTION = `
|
|
|
23731
23923
|
|
|
23732
23924
|
**When to load full checklist:** \`load_skill("factorai-sh")\` before any create/deploy/edit/redeploy task.
|
|
23733
23925
|
|
|
23734
|
-
### Backend URL (
|
|
23926
|
+
### Backend URL (see also \`<sandbox_runtime>\` table)
|
|
23735
23927
|
| Variable | Used by |
|
|
23736
23928
|
|----------|---------|
|
|
23737
23929
|
| \`FACTORAI_BASE_URL\` | \`factorai.sh.get_app_status\`, \`factorai.sh.apply_app_changes\`, \`factorai.sh.redeploy_app\` |
|
|
23738
23930
|
| \`SEVERINO_URL\` | \`factorai.sh.deploy_app\` (ZIP upload to \`/api/v1/deploy\`) |
|
|
23739
23931
|
| \`FACTORAI_API_KEY\` / \`SEVERINO_API_KEY\` | Optional Bearer on write endpoints |
|
|
23740
23932
|
|
|
23741
|
-
The orchestrator configures these \u2014 **use the values
|
|
23933
|
+
The orchestrator configures these \u2014 **use the values in \`<sandbox_runtime>\`**, not invented hosts. Autonomous loop: \`tsc --noEmit\` locally \u2192 \`deploy_app\` \u2192 \`get_app_status({ wait: true })\` \u2192 fix on \`failed\` \u2192 only then \`message(result)\`.
|
|
23742
23934
|
|
|
23743
23935
|
### Source of truth: \`factorai.sh.json\`
|
|
23744
23936
|
- Written/updated by \`factorai.sh.deploy_app\` and FactorAI tools \u2014 **read with normal file tools**, never a separate manifest tool.
|
|
@@ -23791,11 +23983,11 @@ Use \`factorai.sh.redeploy_app({ appId })\` only when sources on the server are
|
|
|
23791
23983
|
|
|
23792
23984
|
## Polling after deploy / apply_app_changes
|
|
23793
23985
|
|
|
23794
|
-
1. \`factorai.sh.get_app_status({ appId })\` or
|
|
23986
|
+
1. **Always** \`factorai.sh.get_app_status({ appId, wait: true })\` after \`deploy_app\` or \`apply_app_changes\` (polls at\xE9 \`ready\` ou \`failed\`).
|
|
23795
23987
|
2. Expect \`status: "building"\` during \`npm install\` / \`next build\` (~30\u201390s).
|
|
23796
|
-
3. **Do not** report "live"
|
|
23797
|
-
4.
|
|
23798
|
-
5.
|
|
23988
|
+
3. **Do not** report "live" until \`success: true\` and \`status: "ready"\` no resultado do \`get_app_status\`.
|
|
23989
|
+
4. On \`success: false\` / \`status: "failed"\`: read \`typescriptErrors\`, \`buildLogTail\`, and \`summary\` from the tool result \u2014 fix the code like a human reading the Next build log, then \`apply_app_changes\` or \`deploy_app\` again.
|
|
23990
|
+
5. Never ignore deploy failures; never tell the user the app is online when \`get_app_status\` returned \`failed\`.
|
|
23799
23991
|
|
|
23800
23992
|
---
|
|
23801
23993
|
|
|
@@ -23821,6 +24013,146 @@ function isFactorAiShPromptEnabled() {
|
|
|
23821
24013
|
return isSandbox && Boolean(baseUrl);
|
|
23822
24014
|
}
|
|
23823
24015
|
|
|
24016
|
+
// src/app/agent/core/prompt/sandbox_autonomy_prompt.ts
|
|
24017
|
+
var SANDBOX_AUTONOMY_SECTION = `
|
|
24018
|
+
<sandbox_autonomy>
|
|
24019
|
+
## Modo aut\xF3nomo (como Manus / agente de engenharia)
|
|
24020
|
+
|
|
24021
|
+
Tu **n\xE3o** est\xE1s num chat interativo com humano ao teclado. Est\xE1s num **worker** com um objetivo, ferramentas, e um prazo. Comportamento esperado:
|
|
24022
|
+
|
|
24023
|
+
### 1. Orientar-te (sempre primeiro)
|
|
24024
|
+
- L\xEA \`<sandbox_runtime>\` \u2014 session, cwd, URLs, pedido, \`factorai.sh.json\` se existir.
|
|
24025
|
+
- Se o pedido envolve Next.js / site / deploy: \`load_skill("factorai-sh")\`.
|
|
24026
|
+
- Lista o diret\xF3rio relevante (\`ls_tool\`) antes de assumir estrutura de ficheiros.
|
|
24027
|
+
|
|
24028
|
+
### 2. Planear em sil\xEAncio \xFAtil
|
|
24029
|
+
- Usa \`todo\` ou \`task_create\` para 3\u20138 passos concretos (n\xE3o gen\xE9ricos).
|
|
24030
|
+
- \`message({ message_type: "info" })\` a cada fase importante \u2014 o orquestrador v\xEA progresso.
|
|
24031
|
+
|
|
24032
|
+
### 3. Implementar como humano s\xE9nior
|
|
24033
|
+
- Edita com \`edit_tool\` / \`file_write\`; l\xEA ficheiros completos antes de patch grande.
|
|
24034
|
+
- UI Next: \`@/components/ui/*\` do scaffold; n\xE3o reinventar componentes.
|
|
24035
|
+
- Subtarefas paralelas: \`spawn_agent\` + \`wait_agent\` quando fizer sentido.
|
|
24036
|
+
|
|
24037
|
+
### 4. Verificar **antes** de deploy (obrigat\xF3rio para apps Next)
|
|
24038
|
+
No diret\xF3rio do projeto (ex. \`./my-app\`):
|
|
24039
|
+
1. \`npx tsc --noEmit\` \u2014 corrige todos os erros TypeScript.
|
|
24040
|
+
2. Opcional: \`npm run lint\` se existir script.
|
|
24041
|
+
3. **Evita** \`npm run build\` local **antes do primeiro** \`deploy_app\` (cria \`.next/\` e incha o ZIP). O build de produ\xE7\xE3o corre no FactorAI.sh.
|
|
24042
|
+
|
|
24043
|
+
### 5. Deploy e valida\xE7\xE3o remota
|
|
24044
|
+
1. \`factorai.sh.deploy_app({ projectDir, name })\` \u2014 primeiro deploy.
|
|
24045
|
+
2. \`factorai.sh.get_app_status({ appId, wait: true })\` \u2014 espera \`ready\` ou \`failed\`.
|
|
24046
|
+
3. Se \`failed\`: l\xEA \`typescriptErrors\`, \`buildLogTail\`, \`summary\` no resultado; corrige c\xF3digo; \`apply_app_changes\` ou novo deploy; **nunca** digas que est\xE1 online.
|
|
24047
|
+
4. S\xF3 depois de \`ready\`: confirma URL (curl ou l\xF3gica) e \`message(result)\` com \`factor-sh-url-app\`.
|
|
24048
|
+
|
|
24049
|
+
### 6. Edi\xE7\xF5es em app j\xE1 online (esta sess\xE3o)
|
|
24050
|
+
- \`factorai.sh.apply_app_changes\` com ficheiro **completo** em cada \`files[].content\`.
|
|
24051
|
+
- Volta a \`get_app_status({ wait: true })\` ap\xF3s rebuild.
|
|
24052
|
+
|
|
24053
|
+
### 7. Entregar como produto acabado
|
|
24054
|
+
- Ficheiros: \`message(result)\` + \`attachments\` em \`.bluma/artifacts/\`.
|
|
24055
|
+
- App live: \`factor-sh-url-app\` com URL absoluta.
|
|
24056
|
+
- Resumo em portugu\xEAs claro: o que fizeste, URL, pr\xF3ximos passos se falhou.
|
|
24057
|
+
|
|
24058
|
+
### Proibido no sandbox worker
|
|
24059
|
+
- Pedir confirma\xE7\xE3o ao utilizador (\`ask_user_question\`) salvo bloqueio real de credencial em falta no env.
|
|
24060
|
+
- Inventar URLs, appIds, ou hosts \u2014 usa s\xF3 \`<sandbox_runtime>\` e \`factorai.sh.json\`.
|
|
24061
|
+
- \`message(result)\` com sucesso se deploy/build falhou.
|
|
24062
|
+
- Ignorar erros de compila\xE7\xE3o (\u201Cvou deploy na mesma\u201D).
|
|
24063
|
+
</sandbox_autonomy>
|
|
24064
|
+
`;
|
|
24065
|
+
|
|
24066
|
+
// src/app/agent/runtime/sandbox_runtime_context.ts
|
|
24067
|
+
import fs36 from "fs";
|
|
24068
|
+
import path39 from "path";
|
|
24069
|
+
function env2(key) {
|
|
24070
|
+
return String(process.env[key] ?? "").trim();
|
|
24071
|
+
}
|
|
24072
|
+
function listWorkspaceTopLevel(cwd2, max = 24) {
|
|
24073
|
+
try {
|
|
24074
|
+
return fs36.readdirSync(cwd2, { withFileTypes: true }).filter((e) => !e.name.startsWith(".")).slice(0, max).map((e) => e.isDirectory() ? `${e.name}/` : e.name);
|
|
24075
|
+
} catch {
|
|
24076
|
+
return [];
|
|
24077
|
+
}
|
|
24078
|
+
}
|
|
24079
|
+
function buildSandboxRuntimeContextBlock(cwd2) {
|
|
24080
|
+
const sessionId = env2("BLUMA_SESSION_ID") || "unknown";
|
|
24081
|
+
const sandboxName = env2("BLUMA_SANDBOX_NAME") || "sandbox-api";
|
|
24082
|
+
const fromAgent = env2("BLUMA_FROM_AGENT") || "orchestrator";
|
|
24083
|
+
const action = env2("BLUMA_ACTION") || "unknown";
|
|
24084
|
+
const severinoUrl = env2("SEVERINO_URL");
|
|
24085
|
+
const factoraiUrl = env2("FACTORAI_BASE_URL") || env2("FACTORAI_URL") || severinoUrl;
|
|
24086
|
+
const userRequest = env2("BLUMA_USER_REQUEST");
|
|
24087
|
+
const factoraiEnabled = isFactorAiShPromptEnabled();
|
|
24088
|
+
const top = listWorkspaceTopLevel(cwd2);
|
|
24089
|
+
const lines = [
|
|
24090
|
+
"<sandbox_runtime>",
|
|
24091
|
+
"## Ambiente atual (factos reais \u2014 usa isto para te orientares)",
|
|
24092
|
+
"",
|
|
24093
|
+
"Operas como **agente aut\xF3nomo** (estilo Manus): n\xE3o h\xE1 utilizador no terminal a responder. O orquestrador (Severino / sandbox-api) envia o pedido, observa o teu progresso via `message(info)` e s\xF3 encerra o job com `message(result)`.",
|
|
24094
|
+
"",
|
|
24095
|
+
"| Campo | Valor |",
|
|
24096
|
+
"|-------|-------|",
|
|
24097
|
+
`| sandbox | ${sandboxName} |`,
|
|
24098
|
+
`| session_id | ${sessionId} |`,
|
|
24099
|
+
`| workspace (cwd) | ${cwd2} |`,
|
|
24100
|
+
`| delegado por | ${fromAgent} |`,
|
|
24101
|
+
`| action | ${action} |`,
|
|
24102
|
+
`| node | ${process.version} |`,
|
|
24103
|
+
`| auto_approve_tools | sim (sandbox isolado) |`
|
|
24104
|
+
];
|
|
24105
|
+
if (severinoUrl) {
|
|
24106
|
+
lines.push(`| SEVERINO_URL (deploy ZIP) | ${severinoUrl} |`);
|
|
24107
|
+
}
|
|
24108
|
+
if (factoraiUrl) {
|
|
24109
|
+
lines.push(`| FACTORAI_BASE_URL (status/apply/redeploy) | ${factoraiUrl} |`);
|
|
24110
|
+
}
|
|
24111
|
+
if (severinoUrl && factoraiUrl && severinoUrl !== factoraiUrl) {
|
|
24112
|
+
lines.push("");
|
|
24113
|
+
lines.push(
|
|
24114
|
+
"\u26A0\uFE0F SEVERINO_URL e FACTORAI_BASE_URL diferem \u2014 confirma qual backend falhou se `get_app_status` der 404 ap\xF3s deploy."
|
|
24115
|
+
);
|
|
24116
|
+
}
|
|
24117
|
+
if (factoraiEnabled) {
|
|
24118
|
+
lines.push("");
|
|
24119
|
+
lines.push("**FactorAI.sh:** ativo nesta sess\xE3o (`factorai.sh.*` tools dispon\xEDveis).");
|
|
24120
|
+
} else {
|
|
24121
|
+
lines.push("");
|
|
24122
|
+
lines.push(
|
|
24123
|
+
"**FactorAI.sh:** n\xE3o configurado (falta `FACTORAI_BASE_URL` ou `BLUMA_SANDBOX`). Hosting Next.js via factorai.sh indispon\xEDvel at\xE9 o orquestrador definir env."
|
|
24124
|
+
);
|
|
24125
|
+
}
|
|
24126
|
+
if (top.length > 0) {
|
|
24127
|
+
lines.push("");
|
|
24128
|
+
lines.push(`**Raiz do workspace:** ${top.join(", ")}`);
|
|
24129
|
+
}
|
|
24130
|
+
const manifest = readFactorAiWorkspaceManifest(cwd2);
|
|
24131
|
+
if (manifest) {
|
|
24132
|
+
const ctx = manifest.appContext;
|
|
24133
|
+
const appId = ctx?.appId;
|
|
24134
|
+
const liveUrl = resolveFactorShAppLiveUrl(cwd2);
|
|
24135
|
+
lines.push("");
|
|
24136
|
+
lines.push("**App FactorAI nesta sess\xE3o (factorai.sh.json):**");
|
|
24137
|
+
if (appId) lines.push(`- appId: ${appId}`);
|
|
24138
|
+
if (liveUrl) lines.push(`- appUrl: ${liveUrl}`);
|
|
24139
|
+
const appDir = typeof manifest.app === "object" && manifest.app && typeof manifest.app.name === "string" ? manifest.app.name : null;
|
|
24140
|
+
if (appDir) {
|
|
24141
|
+
const projectPath = path39.join(cwd2, appDir);
|
|
24142
|
+
if (fs36.existsSync(projectPath)) {
|
|
24143
|
+
lines.push(`- project_dir: ${projectPath}`);
|
|
24144
|
+
}
|
|
24145
|
+
}
|
|
24146
|
+
}
|
|
24147
|
+
if (userRequest) {
|
|
24148
|
+
lines.push("");
|
|
24149
|
+
lines.push("**Pedido deste turno (resumo):**");
|
|
24150
|
+
lines.push(userRequest.length > 2500 ? `${userRequest.slice(0, 2500)}\u2026` : userRequest);
|
|
24151
|
+
}
|
|
24152
|
+
lines.push("</sandbox_runtime>");
|
|
24153
|
+
return lines.join("\n");
|
|
24154
|
+
}
|
|
24155
|
+
|
|
23824
24156
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
23825
24157
|
function getNodeVersion() {
|
|
23826
24158
|
return process.version;
|
|
@@ -23852,17 +24184,17 @@ function getGitBranch(dir) {
|
|
|
23852
24184
|
}
|
|
23853
24185
|
}
|
|
23854
24186
|
function getPackageManager(dir) {
|
|
23855
|
-
if (
|
|
23856
|
-
if (
|
|
23857
|
-
if (
|
|
23858
|
-
if (
|
|
24187
|
+
if (fs37.existsSync(path40.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
24188
|
+
if (fs37.existsSync(path40.join(dir, "yarn.lock"))) return "yarn";
|
|
24189
|
+
if (fs37.existsSync(path40.join(dir, "bun.lockb"))) return "bun";
|
|
24190
|
+
if (fs37.existsSync(path40.join(dir, "package-lock.json"))) return "npm";
|
|
23859
24191
|
return "unknown";
|
|
23860
24192
|
}
|
|
23861
24193
|
function getProjectType(dir) {
|
|
23862
24194
|
try {
|
|
23863
|
-
const files =
|
|
24195
|
+
const files = fs37.readdirSync(dir);
|
|
23864
24196
|
if (files.includes("package.json")) {
|
|
23865
|
-
const pkg = JSON.parse(
|
|
24197
|
+
const pkg = JSON.parse(fs37.readFileSync(path40.join(dir, "package.json"), "utf-8"));
|
|
23866
24198
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
23867
24199
|
if (deps.next) return "Next.js";
|
|
23868
24200
|
if (deps.react) return "React";
|
|
@@ -23882,9 +24214,9 @@ function getProjectType(dir) {
|
|
|
23882
24214
|
}
|
|
23883
24215
|
function getTestFramework(dir) {
|
|
23884
24216
|
try {
|
|
23885
|
-
const pkgPath =
|
|
23886
|
-
if (
|
|
23887
|
-
const pkg = JSON.parse(
|
|
24217
|
+
const pkgPath = path40.join(dir, "package.json");
|
|
24218
|
+
if (fs37.existsSync(pkgPath)) {
|
|
24219
|
+
const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
|
|
23888
24220
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
23889
24221
|
if (deps.jest) return "jest";
|
|
23890
24222
|
if (deps.vitest) return "vitest";
|
|
@@ -23893,7 +24225,7 @@ function getTestFramework(dir) {
|
|
|
23893
24225
|
if (deps["@playwright/test"]) return "playwright";
|
|
23894
24226
|
if (deps.cypress) return "cypress";
|
|
23895
24227
|
}
|
|
23896
|
-
if (
|
|
24228
|
+
if (fs37.existsSync(path40.join(dir, "pytest.ini")) || fs37.existsSync(path40.join(dir, "conftest.py"))) return "pytest";
|
|
23897
24229
|
return "unknown";
|
|
23898
24230
|
} catch {
|
|
23899
24231
|
return "unknown";
|
|
@@ -23901,9 +24233,9 @@ function getTestFramework(dir) {
|
|
|
23901
24233
|
}
|
|
23902
24234
|
function getTestCommand(dir) {
|
|
23903
24235
|
try {
|
|
23904
|
-
const pkgPath =
|
|
23905
|
-
if (
|
|
23906
|
-
const pkg = JSON.parse(
|
|
24236
|
+
const pkgPath = path40.join(dir, "package.json");
|
|
24237
|
+
if (fs37.existsSync(pkgPath)) {
|
|
24238
|
+
const pkg = JSON.parse(fs37.readFileSync(pkgPath, "utf-8"));
|
|
23907
24239
|
if (pkg.scripts?.test) return "npm test";
|
|
23908
24240
|
if (pkg.scripts?.["test:unit"]) return "npm run test:unit";
|
|
23909
24241
|
}
|
|
@@ -23918,8 +24250,8 @@ function getTestCommand(dir) {
|
|
|
23918
24250
|
}
|
|
23919
24251
|
function isGitRepo(dir) {
|
|
23920
24252
|
try {
|
|
23921
|
-
const p =
|
|
23922
|
-
return
|
|
24253
|
+
const p = path40.join(dir, ".git");
|
|
24254
|
+
return fs37.existsSync(p) && fs37.lstatSync(p).isDirectory();
|
|
23923
24255
|
} catch {
|
|
23924
24256
|
return false;
|
|
23925
24257
|
}
|
|
@@ -24130,7 +24462,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
24130
24462
|
const runtimeConfig = getRuntimeConfig();
|
|
24131
24463
|
const cwd2 = process.cwd();
|
|
24132
24464
|
const isSandbox = process.env.BLUMA_SANDBOX === "true";
|
|
24133
|
-
const
|
|
24465
|
+
const env3 = {
|
|
24134
24466
|
os_type: os23.type(),
|
|
24135
24467
|
os_version: os23.release(),
|
|
24136
24468
|
architecture: os23.arch(),
|
|
@@ -24155,7 +24487,7 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
24155
24487
|
session_id: process.env.BLUMA_SESSION_ID || "unknown",
|
|
24156
24488
|
workspace_root: cwd2
|
|
24157
24489
|
};
|
|
24158
|
-
const interpolate = (template) => Object.entries(
|
|
24490
|
+
const interpolate = (template) => Object.entries(env3).reduce((s, [k, v]) => s.replaceAll(`{${k}}`, v), template);
|
|
24159
24491
|
let prompt = interpolate(SYSTEM_PROMPT).replace(
|
|
24160
24492
|
"<<<BLUMA_WORKSPACE_SNAPSHOT_BODY>>>",
|
|
24161
24493
|
buildWorkspaceSnapshot(cwd2)
|
|
@@ -24174,6 +24506,12 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
24174
24506
|
prompt += `
|
|
24175
24507
|
|
|
24176
24508
|
${SUBJECT_MACHINE_MODEL_XML}`;
|
|
24509
|
+
prompt += `
|
|
24510
|
+
|
|
24511
|
+
${buildSandboxRuntimeContextBlock(cwd2)}`;
|
|
24512
|
+
prompt += `
|
|
24513
|
+
|
|
24514
|
+
${SANDBOX_AUTONOMY_SECTION}`;
|
|
24177
24515
|
prompt += interpolate(SANDBOX_SECTION);
|
|
24178
24516
|
if (isFactorAiShPromptEnabled()) {
|
|
24179
24517
|
prompt += `
|
|
@@ -25353,9 +25691,9 @@ var LLMService = class {
|
|
|
25353
25691
|
};
|
|
25354
25692
|
|
|
25355
25693
|
// src/app/agent/utils/user_message_images.ts
|
|
25356
|
-
import
|
|
25694
|
+
import fs38 from "fs";
|
|
25357
25695
|
import os25 from "os";
|
|
25358
|
-
import
|
|
25696
|
+
import path41 from "path";
|
|
25359
25697
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
25360
25698
|
var IMAGE_EXT = /\.(png|jpe?g|gif|webp|bmp)$/i;
|
|
25361
25699
|
var MAX_IMAGE_BYTES = 4 * 1024 * 1024;
|
|
@@ -25371,22 +25709,22 @@ var MIME = {
|
|
|
25371
25709
|
function expandUserPath(p) {
|
|
25372
25710
|
const t = p.trim();
|
|
25373
25711
|
if (t.startsWith("~")) {
|
|
25374
|
-
return
|
|
25712
|
+
return path41.join(os25.homedir(), t.slice(1).replace(/^\//, ""));
|
|
25375
25713
|
}
|
|
25376
25714
|
return t;
|
|
25377
25715
|
}
|
|
25378
25716
|
function isPathAllowed(absResolved, cwd2) {
|
|
25379
|
-
const resolved =
|
|
25380
|
-
const cwdR =
|
|
25381
|
-
const homeR =
|
|
25382
|
-
const tmpR =
|
|
25383
|
-
const underCwd = resolved === cwdR || resolved.startsWith(cwdR +
|
|
25384
|
-
const underHome = resolved === homeR || resolved.startsWith(homeR +
|
|
25385
|
-
const underTmp = resolved === tmpR || resolved.startsWith(tmpR +
|
|
25717
|
+
const resolved = path41.normalize(path41.resolve(absResolved));
|
|
25718
|
+
const cwdR = path41.normalize(path41.resolve(cwd2));
|
|
25719
|
+
const homeR = path41.normalize(path41.resolve(os25.homedir()));
|
|
25720
|
+
const tmpR = path41.normalize(path41.resolve(os25.tmpdir()));
|
|
25721
|
+
const underCwd = resolved === cwdR || resolved.startsWith(cwdR + path41.sep);
|
|
25722
|
+
const underHome = resolved === homeR || resolved.startsWith(homeR + path41.sep);
|
|
25723
|
+
const underTmp = resolved === tmpR || resolved.startsWith(tmpR + path41.sep);
|
|
25386
25724
|
return underCwd || underHome || underTmp;
|
|
25387
25725
|
}
|
|
25388
25726
|
function mimeFor(abs) {
|
|
25389
|
-
const ext =
|
|
25727
|
+
const ext = path41.extname(abs).toLowerCase();
|
|
25390
25728
|
return MIME[ext] || "application/octet-stream";
|
|
25391
25729
|
}
|
|
25392
25730
|
var IMAGE_EXT_SRC = String.raw`(?:png|jpe?g|gif|webp|bmp)`;
|
|
@@ -25430,10 +25768,10 @@ function collectImagePathStrings(raw) {
|
|
|
25430
25768
|
}
|
|
25431
25769
|
function resolveImagePath(candidate, cwd2) {
|
|
25432
25770
|
const expanded = expandUserPath(candidate);
|
|
25433
|
-
const abs =
|
|
25771
|
+
const abs = path41.isAbsolute(expanded) ? path41.normalize(expanded) : path41.normalize(path41.resolve(cwd2, expanded));
|
|
25434
25772
|
if (!isPathAllowed(abs, cwd2)) return null;
|
|
25435
25773
|
try {
|
|
25436
|
-
if (!
|
|
25774
|
+
if (!fs38.existsSync(abs) || !fs38.statSync(abs).isFile()) return null;
|
|
25437
25775
|
} catch {
|
|
25438
25776
|
return null;
|
|
25439
25777
|
}
|
|
@@ -25453,11 +25791,11 @@ function trySingleLineFileUriOrBareImagePath(line, cwd2) {
|
|
|
25453
25791
|
if (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'")) {
|
|
25454
25792
|
s = s.slice(1, -1).trim();
|
|
25455
25793
|
}
|
|
25456
|
-
if (!IMAGE_EXT.test(
|
|
25794
|
+
if (!IMAGE_EXT.test(path41.extname(s))) return null;
|
|
25457
25795
|
const abs = resolveImagePath(s, cwd2);
|
|
25458
25796
|
if (!abs) return null;
|
|
25459
25797
|
try {
|
|
25460
|
-
const st =
|
|
25798
|
+
const st = fs38.statSync(abs);
|
|
25461
25799
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
25462
25800
|
return null;
|
|
25463
25801
|
}
|
|
@@ -25492,7 +25830,7 @@ function tryPasteChunkAsSingleImagePath(chunk, cwd2) {
|
|
|
25492
25830
|
return null;
|
|
25493
25831
|
}
|
|
25494
25832
|
try {
|
|
25495
|
-
const st =
|
|
25833
|
+
const st = fs38.statSync(abs);
|
|
25496
25834
|
if (!st.isFile() || st.size > MAX_IMAGE_BYTES) {
|
|
25497
25835
|
return null;
|
|
25498
25836
|
}
|
|
@@ -25521,7 +25859,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25521
25859
|
const abs = resolveImagePath(c, cwd2);
|
|
25522
25860
|
if (!abs) continue;
|
|
25523
25861
|
try {
|
|
25524
|
-
const st =
|
|
25862
|
+
const st = fs38.statSync(abs);
|
|
25525
25863
|
if (st.size > MAX_IMAGE_BYTES) continue;
|
|
25526
25864
|
} catch {
|
|
25527
25865
|
continue;
|
|
@@ -25538,7 +25876,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25538
25876
|
}
|
|
25539
25877
|
const parts = [{ type: "text", text: textPart }];
|
|
25540
25878
|
for (const abs of resolvedAbs) {
|
|
25541
|
-
const buf =
|
|
25879
|
+
const buf = fs38.readFileSync(abs);
|
|
25542
25880
|
const b64 = buf.toString("base64");
|
|
25543
25881
|
const mime = mimeFor(abs);
|
|
25544
25882
|
parts.push({
|
|
@@ -25556,7 +25894,7 @@ function buildUserMessageContent(raw, cwd2) {
|
|
|
25556
25894
|
init_sandbox_policy();
|
|
25557
25895
|
init_runtime_config();
|
|
25558
25896
|
init_permission_rules();
|
|
25559
|
-
import
|
|
25897
|
+
import path42 from "path";
|
|
25560
25898
|
var LOCAL_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["edit_tool", "file_write", "notebook_edit"]);
|
|
25561
25899
|
function getToolPermissionLayer(metadata) {
|
|
25562
25900
|
if (metadata.riskLevel === "safe") return "read";
|
|
@@ -25571,11 +25909,11 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
25571
25909
|
if (!filePath) {
|
|
25572
25910
|
return { allowed: false, reason: "No file path provided for permission check." };
|
|
25573
25911
|
}
|
|
25574
|
-
const resolvedPath =
|
|
25912
|
+
const resolvedPath = path42.resolve(filePath);
|
|
25575
25913
|
if (!isPathInsideWorkspace(resolvedPath, policy)) {
|
|
25576
25914
|
return { allowed: false, reason: `File path "${filePath}" is outside workspace root.` };
|
|
25577
25915
|
}
|
|
25578
|
-
const relativePath =
|
|
25916
|
+
const relativePath = path42.relative(policy.workspaceRoot, resolvedPath);
|
|
25579
25917
|
const toolPattern = `${toolName}(${relativePath})`;
|
|
25580
25918
|
const ruleDecision = permissionRulesEngine.checkPermission(toolPattern, { filepath: filePath });
|
|
25581
25919
|
if (ruleDecision === "deny") {
|
|
@@ -25584,7 +25922,7 @@ function checkFilePermissionRules(toolName, filePath, policy) {
|
|
|
25584
25922
|
if (ruleDecision === "allow") {
|
|
25585
25923
|
return { allowed: true, reason: `File "${filePath}" allowed by permission rules.` };
|
|
25586
25924
|
}
|
|
25587
|
-
const dirPath =
|
|
25925
|
+
const dirPath = path42.dirname(relativePath);
|
|
25588
25926
|
const dirPattern = `${toolName}(${dirPath}/**)`;
|
|
25589
25927
|
const dirRuleDecision = permissionRulesEngine.checkPermission(dirPattern, { filepath: filePath });
|
|
25590
25928
|
if (dirRuleDecision === "allow") {
|
|
@@ -25801,11 +26139,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
25801
26139
|
}
|
|
25802
26140
|
|
|
25803
26141
|
// src/app/agent/tools/CodingMemoryTool/CodingMemoryConsolidate.ts
|
|
25804
|
-
import * as
|
|
25805
|
-
import * as
|
|
26142
|
+
import * as fs39 from "fs";
|
|
26143
|
+
import * as path43 from "path";
|
|
25806
26144
|
import os26 from "os";
|
|
25807
26145
|
function memoryPath2() {
|
|
25808
|
-
return
|
|
26146
|
+
return path43.join(process.env.HOME || os26.homedir(), ".bluma", "coding_memory.json");
|
|
25809
26147
|
}
|
|
25810
26148
|
function normalizeNote2(note) {
|
|
25811
26149
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -25815,18 +26153,18 @@ function uniqTags(a, b) {
|
|
|
25815
26153
|
}
|
|
25816
26154
|
function consolidateCodingMemoryFile() {
|
|
25817
26155
|
const p = memoryPath2();
|
|
25818
|
-
if (!
|
|
26156
|
+
if (!fs39.existsSync(p)) {
|
|
25819
26157
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
25820
26158
|
}
|
|
25821
26159
|
const bak = `${p}.bak`;
|
|
25822
26160
|
try {
|
|
25823
|
-
|
|
26161
|
+
fs39.copyFileSync(p, bak);
|
|
25824
26162
|
} catch (e) {
|
|
25825
26163
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
25826
26164
|
}
|
|
25827
26165
|
let data;
|
|
25828
26166
|
try {
|
|
25829
|
-
data = JSON.parse(
|
|
26167
|
+
data = JSON.parse(fs39.readFileSync(p, "utf-8"));
|
|
25830
26168
|
} catch (e) {
|
|
25831
26169
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
25832
26170
|
}
|
|
@@ -25861,7 +26199,7 @@ function consolidateCodingMemoryFile() {
|
|
|
25861
26199
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
25862
26200
|
};
|
|
25863
26201
|
try {
|
|
25864
|
-
|
|
26202
|
+
fs39.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
25865
26203
|
} catch (e) {
|
|
25866
26204
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
25867
26205
|
}
|
|
@@ -26367,18 +26705,18 @@ var BluMaToolRunner = class {
|
|
|
26367
26705
|
|
|
26368
26706
|
// src/app/agent/session_manager/session_archive.ts
|
|
26369
26707
|
init_bluma_app_dir();
|
|
26370
|
-
import
|
|
26371
|
-
import { promises as
|
|
26708
|
+
import path44 from "path";
|
|
26709
|
+
import { promises as fs40 } from "fs";
|
|
26372
26710
|
async function archivePrunedConversationMessages(sessionId, messages) {
|
|
26373
26711
|
if (!sessionId || messages.length === 0) {
|
|
26374
26712
|
return null;
|
|
26375
26713
|
}
|
|
26376
26714
|
const appDir = getPreferredAppDir();
|
|
26377
|
-
const dir =
|
|
26378
|
-
await
|
|
26379
|
-
const archiveFile =
|
|
26715
|
+
const dir = path44.join(appDir, "sessions", "archive", sessionId);
|
|
26716
|
+
await fs40.mkdir(dir, { recursive: true });
|
|
26717
|
+
const archiveFile = path44.join(dir, `${Date.now()}.jsonl`);
|
|
26380
26718
|
const lines = messages.map((m) => JSON.stringify(m)).join("\n") + "\n";
|
|
26381
|
-
await
|
|
26719
|
+
await fs40.appendFile(archiveFile, lines, "utf-8");
|
|
26382
26720
|
return archiveFile;
|
|
26383
26721
|
}
|
|
26384
26722
|
|
|
@@ -27239,7 +27577,7 @@ Update existing files instead of duplicating.` : "";
|
|
|
27239
27577
|
|
|
27240
27578
|
// src/app/agent/memory/memory_tool_policy.ts
|
|
27241
27579
|
init_paths();
|
|
27242
|
-
import
|
|
27580
|
+
import path45 from "path";
|
|
27243
27581
|
var MEMORY_READ_TOOLS = /* @__PURE__ */ new Set([
|
|
27244
27582
|
"read_file_lines",
|
|
27245
27583
|
"grep_search",
|
|
@@ -27266,7 +27604,7 @@ function isReadOnlyShellCommand(command) {
|
|
|
27266
27604
|
return READ_ONLY_SHELL.test(trimmed);
|
|
27267
27605
|
}
|
|
27268
27606
|
function createAutoMemToolGate(memoryDir) {
|
|
27269
|
-
const memRoot = memoryDir.endsWith(
|
|
27607
|
+
const memRoot = memoryDir.endsWith(path45.sep) ? memoryDir : memoryDir + path45.sep;
|
|
27270
27608
|
return (toolName, args) => {
|
|
27271
27609
|
if (MEMORY_READ_TOOLS.has(toolName)) {
|
|
27272
27610
|
return { allowed: true };
|
|
@@ -27279,7 +27617,7 @@ function createAutoMemToolGate(memoryDir) {
|
|
|
27279
27617
|
}
|
|
27280
27618
|
if (MEMORY_WRITE_TOOLS.has(toolName)) {
|
|
27281
27619
|
const fp = extractFilePath(args);
|
|
27282
|
-
if (fp && isAutoMemPath(
|
|
27620
|
+
if (fp && isAutoMemPath(path45.resolve(fp))) {
|
|
27283
27621
|
return { allowed: true };
|
|
27284
27622
|
}
|
|
27285
27623
|
return { allowed: false, reason: `Writes must stay under ${memRoot}` };
|
|
@@ -27288,18 +27626,18 @@ function createAutoMemToolGate(memoryDir) {
|
|
|
27288
27626
|
};
|
|
27289
27627
|
}
|
|
27290
27628
|
function createSessionMemoryToolGate(sessionMemoryPath) {
|
|
27291
|
-
const resolvedTarget =
|
|
27629
|
+
const resolvedTarget = path45.resolve(sessionMemoryPath);
|
|
27292
27630
|
return (toolName, args) => {
|
|
27293
27631
|
if (toolName === "read_file_lines") {
|
|
27294
27632
|
const fp = extractFilePath(args);
|
|
27295
|
-
if (fp &&
|
|
27633
|
+
if (fp && path45.resolve(fp) === resolvedTarget) {
|
|
27296
27634
|
return { allowed: true };
|
|
27297
27635
|
}
|
|
27298
27636
|
return { allowed: false, reason: "Session memory subagent may only read the session summary file" };
|
|
27299
27637
|
}
|
|
27300
27638
|
if (toolName === "edit_tool") {
|
|
27301
27639
|
const fp = extractFilePath(args);
|
|
27302
|
-
if (fp &&
|
|
27640
|
+
if (fp && path45.resolve(fp) === resolvedTarget) {
|
|
27303
27641
|
return { allowed: true };
|
|
27304
27642
|
}
|
|
27305
27643
|
return { allowed: false, reason: "Session memory subagent may only edit the session summary file" };
|
|
@@ -27326,7 +27664,7 @@ function hasAutoMemWritesSinceHistory(history, sinceIndex) {
|
|
|
27326
27664
|
continue;
|
|
27327
27665
|
}
|
|
27328
27666
|
const fp = extractFilePath(args);
|
|
27329
|
-
if (fp && isAutoMemPath(
|
|
27667
|
+
if (fp && isAutoMemPath(path45.resolve(fp))) {
|
|
27330
27668
|
return true;
|
|
27331
27669
|
}
|
|
27332
27670
|
}
|
|
@@ -27794,13 +28132,13 @@ var BluMaAgent = class {
|
|
|
27794
28132
|
if (!this.sessionFile) return;
|
|
27795
28133
|
try {
|
|
27796
28134
|
const sessionData = {
|
|
27797
|
-
session_id:
|
|
28135
|
+
session_id: path46.basename(this.sessionFile, ".json"),
|
|
27798
28136
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27799
28137
|
conversation_history: this.history,
|
|
27800
28138
|
last_updated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27801
28139
|
...this.compressor.getSnapshot()
|
|
27802
28140
|
};
|
|
27803
|
-
|
|
28141
|
+
fs41.writeFileSync(this.sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
27804
28142
|
} catch (error) {
|
|
27805
28143
|
console.error("[Bluma] Failed to persist session synchronously:", error);
|
|
27806
28144
|
}
|
|
@@ -28006,7 +28344,7 @@ var BluMaAgent = class {
|
|
|
28006
28344
|
|
|
28007
28345
|
${editData.error.display}`;
|
|
28008
28346
|
}
|
|
28009
|
-
const filename =
|
|
28347
|
+
const filename = path46.basename(toolArgs.file_path);
|
|
28010
28348
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
28011
28349
|
} catch (e) {
|
|
28012
28350
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -28741,16 +29079,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
28741
29079
|
}
|
|
28742
29080
|
|
|
28743
29081
|
// src/app/agent/core/memory/session_memory.ts
|
|
28744
|
-
import
|
|
29082
|
+
import fs42 from "fs";
|
|
28745
29083
|
import os29 from "os";
|
|
28746
|
-
import
|
|
29084
|
+
import path47 from "path";
|
|
28747
29085
|
import { v4 as uuidv49 } from "uuid";
|
|
28748
29086
|
var SessionMemoryExtractor = class {
|
|
28749
29087
|
llmClient;
|
|
28750
29088
|
memoryFile;
|
|
28751
29089
|
constructor(options = {}) {
|
|
28752
29090
|
this.llmClient = options.llmClient;
|
|
28753
|
-
this.memoryFile = options.memoryFile ||
|
|
29091
|
+
this.memoryFile = options.memoryFile || path47.join(os29.homedir(), ".bluma", "session_memory.json");
|
|
28754
29092
|
}
|
|
28755
29093
|
/**
|
|
28756
29094
|
* Extract memories from conversation using LLM
|
|
@@ -28807,15 +29145,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
28807
29145
|
);
|
|
28808
29146
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
28809
29147
|
const trimmed = unique.slice(0, 200);
|
|
28810
|
-
|
|
29148
|
+
fs42.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
28811
29149
|
}
|
|
28812
29150
|
/**
|
|
28813
29151
|
* Load memories from disk
|
|
28814
29152
|
*/
|
|
28815
29153
|
async loadMemories() {
|
|
28816
29154
|
try {
|
|
28817
|
-
if (!
|
|
28818
|
-
const data =
|
|
29155
|
+
if (!fs42.existsSync(this.memoryFile)) return [];
|
|
29156
|
+
const data = fs42.readFileSync(this.memoryFile, "utf-8");
|
|
28819
29157
|
return JSON.parse(data);
|
|
28820
29158
|
} catch {
|
|
28821
29159
|
return [];
|
|
@@ -29359,14 +29697,14 @@ var RouteManager = class {
|
|
|
29359
29697
|
this.subAgents = subAgents;
|
|
29360
29698
|
this.core = core;
|
|
29361
29699
|
}
|
|
29362
|
-
registerRoute(
|
|
29363
|
-
this.routeHandlers.set(
|
|
29700
|
+
registerRoute(path60, handler) {
|
|
29701
|
+
this.routeHandlers.set(path60, handler);
|
|
29364
29702
|
}
|
|
29365
29703
|
async handleRoute(payload) {
|
|
29366
29704
|
const inputText = String(payload.content || "").trim();
|
|
29367
29705
|
const { userContext, options } = payload;
|
|
29368
|
-
for (const [
|
|
29369
|
-
if (inputText ===
|
|
29706
|
+
for (const [path60, handler] of this.routeHandlers) {
|
|
29707
|
+
if (inputText === path60 || inputText.startsWith(`${path60} `)) {
|
|
29370
29708
|
return handler({ content: inputText, userContext });
|
|
29371
29709
|
}
|
|
29372
29710
|
}
|
|
@@ -29375,13 +29713,13 @@ var RouteManager = class {
|
|
|
29375
29713
|
};
|
|
29376
29714
|
|
|
29377
29715
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
29378
|
-
import
|
|
29716
|
+
import path48 from "path";
|
|
29379
29717
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
29380
29718
|
async function loadPluginsAtStartup() {
|
|
29381
29719
|
for (const p of listPlugins()) {
|
|
29382
29720
|
const entry = p.manifest.entry?.trim();
|
|
29383
29721
|
if (!entry) continue;
|
|
29384
|
-
const abs =
|
|
29722
|
+
const abs = path48.resolve(p.root, entry);
|
|
29385
29723
|
try {
|
|
29386
29724
|
const href = pathToFileURL2(abs).href;
|
|
29387
29725
|
const mod = await import(href);
|
|
@@ -29402,7 +29740,7 @@ async function loadPluginsAtStartup() {
|
|
|
29402
29740
|
}
|
|
29403
29741
|
|
|
29404
29742
|
// src/app/agent/agent.ts
|
|
29405
|
-
var globalEnvPath =
|
|
29743
|
+
var globalEnvPath = path49.join(os30.homedir(), ".bluma", ".env");
|
|
29406
29744
|
dotenv.config({ path: globalEnvPath });
|
|
29407
29745
|
var Agent = class {
|
|
29408
29746
|
sessionId;
|
|
@@ -31258,10 +31596,10 @@ function resolveToolPayload(result) {
|
|
|
31258
31596
|
|
|
31259
31597
|
// src/app/ui/components/FilePathLink.tsx
|
|
31260
31598
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
31261
|
-
import
|
|
31599
|
+
import path51 from "node:path";
|
|
31262
31600
|
|
|
31263
31601
|
// src/app/ui/utils/pathDisplay.ts
|
|
31264
|
-
import
|
|
31602
|
+
import path50 from "node:path";
|
|
31265
31603
|
import os31 from "node:os";
|
|
31266
31604
|
function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
31267
31605
|
let s = String(pathInput ?? "").trim();
|
|
@@ -31269,17 +31607,17 @@ function formatPathForDisplay(pathInput, cwd2 = process.cwd()) {
|
|
|
31269
31607
|
s = s.replace(/[/\\]+$/, "");
|
|
31270
31608
|
}
|
|
31271
31609
|
if (!s) return "";
|
|
31272
|
-
const abs =
|
|
31273
|
-
const resolvedCwd =
|
|
31274
|
-
const rel =
|
|
31275
|
-
if (rel === "" || !rel.startsWith("..") && !
|
|
31610
|
+
const abs = path50.isAbsolute(s) ? path50.normalize(s) : path50.resolve(cwd2, s);
|
|
31611
|
+
const resolvedCwd = path50.resolve(cwd2);
|
|
31612
|
+
const rel = path50.relative(resolvedCwd, abs);
|
|
31613
|
+
if (rel === "" || !rel.startsWith("..") && !path50.isAbsolute(rel)) {
|
|
31276
31614
|
return rel === "" ? "." : rel;
|
|
31277
31615
|
}
|
|
31278
|
-
const home =
|
|
31279
|
-
if (abs === home || abs.startsWith(home +
|
|
31616
|
+
const home = path50.normalize(os31.homedir());
|
|
31617
|
+
if (abs === home || abs.startsWith(home + path50.sep)) {
|
|
31280
31618
|
return "~" + abs.slice(home.length);
|
|
31281
31619
|
}
|
|
31282
|
-
return
|
|
31620
|
+
return path50.basename(abs);
|
|
31283
31621
|
}
|
|
31284
31622
|
|
|
31285
31623
|
// src/app/ui/components/FilePathLink.tsx
|
|
@@ -31289,7 +31627,7 @@ function FilePathLink({ filePath, children, cwd: cwd2 = process.cwd(), color })
|
|
|
31289
31627
|
if (!raw) {
|
|
31290
31628
|
return null;
|
|
31291
31629
|
}
|
|
31292
|
-
const abs =
|
|
31630
|
+
const abs = path51.isAbsolute(raw) ? path51.normalize(raw) : path51.resolve(cwd2, raw);
|
|
31293
31631
|
const href = pathToFileURL3(abs).href;
|
|
31294
31632
|
const label = formatPathForDisplay(abs, cwd2);
|
|
31295
31633
|
const text = typeof children === "string" ? children : label;
|
|
@@ -31910,16 +32248,16 @@ function supportsHyperlinks(options) {
|
|
|
31910
32248
|
if (stdoutSupported) {
|
|
31911
32249
|
return true;
|
|
31912
32250
|
}
|
|
31913
|
-
const
|
|
31914
|
-
const termProgram =
|
|
32251
|
+
const env3 = options?.env ?? process.env;
|
|
32252
|
+
const termProgram = env3["TERM_PROGRAM"];
|
|
31915
32253
|
if (termProgram && ADDITIONAL_HYPERLINK_TERMINALS.includes(termProgram)) {
|
|
31916
32254
|
return true;
|
|
31917
32255
|
}
|
|
31918
|
-
const lcTerminal =
|
|
32256
|
+
const lcTerminal = env3["LC_TERMINAL"];
|
|
31919
32257
|
if (lcTerminal && ADDITIONAL_HYPERLINK_TERMINALS.includes(lcTerminal)) {
|
|
31920
32258
|
return true;
|
|
31921
32259
|
}
|
|
31922
|
-
const term =
|
|
32260
|
+
const term = env3["TERM"];
|
|
31923
32261
|
if (term?.includes("kitty")) {
|
|
31924
32262
|
return true;
|
|
31925
32263
|
}
|
|
@@ -33241,12 +33579,12 @@ function patchToUnifiedDiffText(filePath, patch) {
|
|
|
33241
33579
|
}
|
|
33242
33580
|
|
|
33243
33581
|
// src/app/ui/utils/readEditContext.ts
|
|
33244
|
-
import { promises as
|
|
33582
|
+
import { promises as fs43 } from "fs";
|
|
33245
33583
|
var CHUNK_SIZE = 64 * 1024;
|
|
33246
33584
|
var CONTEXT_LINES2 = 3;
|
|
33247
33585
|
async function openForScan(filePath) {
|
|
33248
33586
|
try {
|
|
33249
|
-
return await
|
|
33587
|
+
return await fs43.open(filePath, "r");
|
|
33250
33588
|
} catch (e) {
|
|
33251
33589
|
if (e.code === "ENOENT") return null;
|
|
33252
33590
|
throw e;
|
|
@@ -33924,13 +34262,13 @@ function EditToolDiffPanel({
|
|
|
33924
34262
|
newString,
|
|
33925
34263
|
replaceAll = false
|
|
33926
34264
|
}) {
|
|
33927
|
-
const
|
|
34265
|
+
const path60 = filePath.trim() || "unknown file";
|
|
33928
34266
|
const hasPreviewArgs = oldString !== void 0 && newString !== void 0;
|
|
33929
34267
|
const hasDiffText = diffText && diffText.trim().length > 0;
|
|
33930
34268
|
return /* @__PURE__ */ jsx43(Box_default, { flexDirection: "column", children: hasPreviewArgs ? /* @__PURE__ */ jsx43(Box_default, { marginTop: 0, children: /* @__PURE__ */ jsx43(
|
|
33931
34269
|
FileEditToolDiff,
|
|
33932
34270
|
{
|
|
33933
|
-
filePath:
|
|
34271
|
+
filePath: path60,
|
|
33934
34272
|
oldString,
|
|
33935
34273
|
newString,
|
|
33936
34274
|
replaceAll,
|
|
@@ -33968,7 +34306,7 @@ function renderToolUseMessage12({ args }) {
|
|
|
33968
34306
|
return /* @__PURE__ */ jsx44(Text, { color: BLUMA_TERMINAL.blue, children: p });
|
|
33969
34307
|
}
|
|
33970
34308
|
function renderToolHeader12({ args }) {
|
|
33971
|
-
const
|
|
34309
|
+
const path60 = args?.file_path ?? ".";
|
|
33972
34310
|
const oldText = typeof args?.old_string === "string" ? args.old_string : "";
|
|
33973
34311
|
const newText = typeof args?.new_string === "string" ? args.new_string : "";
|
|
33974
34312
|
const counts = countLineDiff(oldText, newText);
|
|
@@ -33978,7 +34316,7 @@ function renderToolHeader12({ args }) {
|
|
|
33978
34316
|
action,
|
|
33979
34317
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
33980
34318
|
" ",
|
|
33981
|
-
/* @__PURE__ */ jsx44(FilePathLink, { filePath:
|
|
34319
|
+
/* @__PURE__ */ jsx44(FilePathLink, { filePath: path60, color: BLUMA_TERMINAL.dim })
|
|
33982
34320
|
] })
|
|
33983
34321
|
] }),
|
|
33984
34322
|
/* @__PURE__ */ jsxs27(Text, { dimColor: true, children: [
|
|
@@ -34370,11 +34708,11 @@ function userFacingName13() {
|
|
|
34370
34708
|
}
|
|
34371
34709
|
function renderToolUseMessage14({ args }) {
|
|
34372
34710
|
const q = args?.query ? `"${args.query}"` : "...";
|
|
34373
|
-
const
|
|
34711
|
+
const path60 = args?.path || ".";
|
|
34374
34712
|
return /* @__PURE__ */ jsxs30(Box_default, { flexDirection: "row", flexWrap: "wrap", alignItems: "flex-end", children: [
|
|
34375
34713
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.blue, children: q }),
|
|
34376
34714
|
/* @__PURE__ */ jsx47(Text, { color: BLUMA_TERMINAL.dim, children: " " }),
|
|
34377
|
-
/* @__PURE__ */ jsx47(FilePathLink, { filePath:
|
|
34715
|
+
/* @__PURE__ */ jsx47(FilePathLink, { filePath: path60, color: BLUMA_TERMINAL.dim })
|
|
34378
34716
|
] });
|
|
34379
34717
|
}
|
|
34380
34718
|
function renderToolHeader14({ args }) {
|
|
@@ -34896,7 +35234,7 @@ var loadSkillTool = createTool({
|
|
|
34896
35234
|
});
|
|
34897
35235
|
|
|
34898
35236
|
// src/app/agent/tools/FileWriteTool/UI.tsx
|
|
34899
|
-
import
|
|
35237
|
+
import fs44 from "fs";
|
|
34900
35238
|
import { diffLines as diffLines3 } from "diff";
|
|
34901
35239
|
import { jsx as jsx54, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
34902
35240
|
function getFilePath(args) {
|
|
@@ -34911,7 +35249,7 @@ function getFilePath(args) {
|
|
|
34911
35249
|
function readExistingFileText(filePath) {
|
|
34912
35250
|
if (!filePath) return "";
|
|
34913
35251
|
try {
|
|
34914
|
-
return
|
|
35252
|
+
return fs44.readFileSync(filePath, "utf-8");
|
|
34915
35253
|
} catch {
|
|
34916
35254
|
return "";
|
|
34917
35255
|
}
|
|
@@ -37853,8 +38191,8 @@ import {
|
|
|
37853
38191
|
|
|
37854
38192
|
// src/app/ui/hooks/useAtCompletion.ts
|
|
37855
38193
|
import { useEffect as useEffect11, useRef as useRef4, useState as useState13 } from "react";
|
|
37856
|
-
import
|
|
37857
|
-
import
|
|
38194
|
+
import fs45 from "fs";
|
|
38195
|
+
import path52 from "path";
|
|
37858
38196
|
var MAX_RESULTS3 = 50;
|
|
37859
38197
|
var DEFAULT_RECURSIVE_DEPTH = 2;
|
|
37860
38198
|
function listPathSuggestions(baseDir, pattern) {
|
|
@@ -37862,7 +38200,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37862
38200
|
const patternEndsWithSlash = raw.endsWith("/");
|
|
37863
38201
|
const relDir = raw.replace(/^\/+|\/+$/g, "");
|
|
37864
38202
|
const filterPrefix = patternEndsWithSlash ? "" : relDir.split("/").slice(-1)[0] || "";
|
|
37865
|
-
const listDir =
|
|
38203
|
+
const listDir = path52.resolve(baseDir, relDir || ".");
|
|
37866
38204
|
const results = [];
|
|
37867
38205
|
const IGNORED_DIRS = ["node_modules", ".git", ".venv", "dist", "build"];
|
|
37868
38206
|
const IGNORED_EXTS = [".pyc", ".class", ".o", ".map", ".log", ".tmp"];
|
|
@@ -37879,7 +38217,7 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37879
38217
|
}
|
|
37880
38218
|
function pushEntry(entryPath, label, isDir) {
|
|
37881
38219
|
if (results.length >= MAX_RESULTS3) return;
|
|
37882
|
-
const clean = label.split(
|
|
38220
|
+
const clean = label.split(path52.sep).join("/").replace(/[]+/g, "");
|
|
37883
38221
|
results.push({ label: clean + (isDir ? "/" : ""), fullPath: entryPath, isDir });
|
|
37884
38222
|
}
|
|
37885
38223
|
try {
|
|
@@ -37888,11 +38226,11 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37888
38226
|
while (queue.length && results.length < MAX_RESULTS3) {
|
|
37889
38227
|
const node = queue.shift();
|
|
37890
38228
|
try {
|
|
37891
|
-
const entries =
|
|
38229
|
+
const entries = fs45.readdirSync(node.dir, { withFileTypes: true });
|
|
37892
38230
|
for (const entry of entries) {
|
|
37893
38231
|
if (isIgnoredName(entry.name)) continue;
|
|
37894
|
-
const entryAbs =
|
|
37895
|
-
const entryRel = node.rel ?
|
|
38232
|
+
const entryAbs = path52.join(node.dir, entry.name);
|
|
38233
|
+
const entryRel = node.rel ? path52.posix.join(node.rel, entry.name) : entry.name;
|
|
37896
38234
|
if (entryRel.split("/").includes("node_modules")) continue;
|
|
37897
38235
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
37898
38236
|
pushEntry(entryAbs, entryRel, entry.isDirectory());
|
|
@@ -37905,13 +38243,13 @@ function listPathSuggestions(baseDir, pattern) {
|
|
|
37905
38243
|
}
|
|
37906
38244
|
}
|
|
37907
38245
|
} else {
|
|
37908
|
-
const entries =
|
|
38246
|
+
const entries = fs45.readdirSync(listDir, { withFileTypes: true });
|
|
37909
38247
|
for (const entry of entries) {
|
|
37910
38248
|
if (filterPrefix && !entry.name.startsWith(filterPrefix)) continue;
|
|
37911
38249
|
if (isIgnoredName(entry.name)) continue;
|
|
37912
38250
|
if (!entry.isDirectory() && isIgnoredFile(entry.name)) continue;
|
|
37913
|
-
const entryAbs =
|
|
37914
|
-
const label = relDir ?
|
|
38251
|
+
const entryAbs = path52.join(listDir, entry.name);
|
|
38252
|
+
const label = relDir ? path52.posix.join(relDir, entry.name) : entry.name;
|
|
37915
38253
|
pushEntry(entryAbs, label, entry.isDirectory());
|
|
37916
38254
|
if (results.length >= MAX_RESULTS3) break;
|
|
37917
38255
|
}
|
|
@@ -38112,9 +38450,9 @@ var SlashSubmenuInlineComponent = ({ menu }) => {
|
|
|
38112
38450
|
var SlashSubmenuInline = memo15(SlashSubmenuInlineComponent);
|
|
38113
38451
|
|
|
38114
38452
|
// src/app/ui/utils/clipboardImage.ts
|
|
38115
|
-
import
|
|
38453
|
+
import fs46 from "fs";
|
|
38116
38454
|
import os32 from "os";
|
|
38117
|
-
import
|
|
38455
|
+
import path53 from "path";
|
|
38118
38456
|
import { spawn as spawn5, execFile as execFileCb, execSync as execSync4 } from "child_process";
|
|
38119
38457
|
import { promisify as promisify2 } from "util";
|
|
38120
38458
|
|
|
@@ -38243,8 +38581,8 @@ function commandOnPath(cmd) {
|
|
|
38243
38581
|
function unixClipboardHelperDirs() {
|
|
38244
38582
|
const h = os32.homedir();
|
|
38245
38583
|
return [
|
|
38246
|
-
|
|
38247
|
-
|
|
38584
|
+
path53.join(h, ".local", "bin"),
|
|
38585
|
+
path53.join(h, "bin"),
|
|
38248
38586
|
"/usr/bin",
|
|
38249
38587
|
"/usr/local/bin",
|
|
38250
38588
|
"/bin",
|
|
@@ -38262,16 +38600,16 @@ function resolveHelperBinary(cmd) {
|
|
|
38262
38600
|
return cmd;
|
|
38263
38601
|
}
|
|
38264
38602
|
for (const dir of unixClipboardHelperDirs()) {
|
|
38265
|
-
const full =
|
|
38603
|
+
const full = path53.join(dir, cmd);
|
|
38266
38604
|
try {
|
|
38267
|
-
|
|
38605
|
+
fs46.accessSync(full, fs46.constants.X_OK);
|
|
38268
38606
|
return full;
|
|
38269
38607
|
} catch {
|
|
38270
38608
|
}
|
|
38271
38609
|
}
|
|
38272
38610
|
for (const dir of unixClipboardHelperDirs()) {
|
|
38273
|
-
const full =
|
|
38274
|
-
if (
|
|
38611
|
+
const full = path53.join(dir, cmd);
|
|
38612
|
+
if (fs46.existsSync(full)) {
|
|
38275
38613
|
return full;
|
|
38276
38614
|
}
|
|
38277
38615
|
}
|
|
@@ -38312,17 +38650,17 @@ function writeBufferIfImage(baseDir, buf) {
|
|
|
38312
38650
|
if (!ext) {
|
|
38313
38651
|
return null;
|
|
38314
38652
|
}
|
|
38315
|
-
const out =
|
|
38653
|
+
const out = path53.join(
|
|
38316
38654
|
baseDir,
|
|
38317
38655
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
38318
38656
|
);
|
|
38319
|
-
|
|
38657
|
+
fs46.writeFileSync(out, buf);
|
|
38320
38658
|
return out;
|
|
38321
38659
|
}
|
|
38322
38660
|
function unlinkQuiet(p) {
|
|
38323
38661
|
try {
|
|
38324
|
-
if (
|
|
38325
|
-
|
|
38662
|
+
if (fs46.existsSync(p)) {
|
|
38663
|
+
fs46.unlinkSync(p);
|
|
38326
38664
|
}
|
|
38327
38665
|
} catch {
|
|
38328
38666
|
}
|
|
@@ -38339,25 +38677,25 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
38339
38677
|
return null;
|
|
38340
38678
|
}
|
|
38341
38679
|
for (const src of tmpPaths) {
|
|
38342
|
-
if (!src || !
|
|
38680
|
+
if (!src || !fs46.existsSync(src)) {
|
|
38343
38681
|
continue;
|
|
38344
38682
|
}
|
|
38345
38683
|
let st;
|
|
38346
38684
|
try {
|
|
38347
|
-
st =
|
|
38685
|
+
st = fs46.statSync(src);
|
|
38348
38686
|
} catch {
|
|
38349
38687
|
continue;
|
|
38350
38688
|
}
|
|
38351
38689
|
if (st.size < 80 || st.size > CLIPBOARD_MAX_BYTES) {
|
|
38352
38690
|
continue;
|
|
38353
38691
|
}
|
|
38354
|
-
const ext =
|
|
38692
|
+
const ext = path53.extname(src).toLowerCase();
|
|
38355
38693
|
const safeExt = ext && /^\.(png|jpe?g|gif|webp)$/i.test(ext) ? ext : ".png";
|
|
38356
|
-
const out =
|
|
38694
|
+
const out = path53.join(
|
|
38357
38695
|
baseDir,
|
|
38358
38696
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt}`
|
|
38359
38697
|
);
|
|
38360
|
-
|
|
38698
|
+
fs46.copyFileSync(src, out);
|
|
38361
38699
|
for (const p of tmpPaths) {
|
|
38362
38700
|
unlinkQuiet(p);
|
|
38363
38701
|
}
|
|
@@ -38371,14 +38709,14 @@ async function tryDarwinClipboardy(baseDir) {
|
|
|
38371
38709
|
return null;
|
|
38372
38710
|
}
|
|
38373
38711
|
async function tryWindowsPowerShell(outFile) {
|
|
38374
|
-
const ps = process.env.SystemRoot != null ?
|
|
38712
|
+
const ps = process.env.SystemRoot != null ? path53.join(
|
|
38375
38713
|
process.env.SystemRoot,
|
|
38376
38714
|
"System32",
|
|
38377
38715
|
"WindowsPowerShell",
|
|
38378
38716
|
"v1.0",
|
|
38379
38717
|
"powershell.exe"
|
|
38380
38718
|
) : "powershell.exe";
|
|
38381
|
-
if (!
|
|
38719
|
+
if (!fs46.existsSync(ps)) {
|
|
38382
38720
|
return false;
|
|
38383
38721
|
}
|
|
38384
38722
|
const script = "$ErrorActionPreference='Stop';Add-Type -AssemblyName System.Windows.Forms;Add-Type -AssemblyName System.Drawing;$img=[System.Windows.Forms.Clipboard]::GetImage();if($null -eq $img){exit 2};$img.Save($env:BLUMA_CLIP_OUT,[System.Drawing.Imaging.ImageFormat]::Png);exit 0";
|
|
@@ -38400,7 +38738,7 @@ async function tryWindowsPowerShell(outFile) {
|
|
|
38400
38738
|
return false;
|
|
38401
38739
|
}
|
|
38402
38740
|
try {
|
|
38403
|
-
const st =
|
|
38741
|
+
const st = fs46.statSync(outFile);
|
|
38404
38742
|
return st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES;
|
|
38405
38743
|
} catch {
|
|
38406
38744
|
return false;
|
|
@@ -38472,8 +38810,8 @@ function parseClipboardTextAsImagePath(raw) {
|
|
|
38472
38810
|
s = s.slice(1, -1);
|
|
38473
38811
|
}
|
|
38474
38812
|
s = s.trim();
|
|
38475
|
-
if (!CLIPBOARD_PATH_IMAGE_EXT.test(
|
|
38476
|
-
const abs =
|
|
38813
|
+
if (!CLIPBOARD_PATH_IMAGE_EXT.test(path53.extname(s))) return null;
|
|
38814
|
+
const abs = path53.isAbsolute(s) ? path53.normalize(s) : path53.resolve(process.cwd(), s);
|
|
38477
38815
|
return abs;
|
|
38478
38816
|
}
|
|
38479
38817
|
async function tryClipboardTextAsImageFile(baseDir) {
|
|
@@ -38487,19 +38825,19 @@ async function tryClipboardTextAsImageFile(baseDir) {
|
|
|
38487
38825
|
const abs = parseClipboardTextAsImagePath(t);
|
|
38488
38826
|
if (!abs) return null;
|
|
38489
38827
|
try {
|
|
38490
|
-
if (!
|
|
38491
|
-
const st =
|
|
38828
|
+
if (!fs46.existsSync(abs)) return null;
|
|
38829
|
+
const st = fs46.statSync(abs);
|
|
38492
38830
|
if (!st.isFile() || st.size > CLIPBOARD_MAX_BYTES || st.size < 20) return null;
|
|
38493
38831
|
} catch {
|
|
38494
38832
|
return null;
|
|
38495
38833
|
}
|
|
38496
|
-
const ext =
|
|
38497
|
-
const out =
|
|
38834
|
+
const ext = path53.extname(abs).toLowerCase();
|
|
38835
|
+
const out = path53.join(
|
|
38498
38836
|
baseDir,
|
|
38499
38837
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`
|
|
38500
38838
|
);
|
|
38501
38839
|
try {
|
|
38502
|
-
|
|
38840
|
+
fs46.copyFileSync(abs, out);
|
|
38503
38841
|
return out;
|
|
38504
38842
|
} catch {
|
|
38505
38843
|
return null;
|
|
@@ -38509,7 +38847,7 @@ async function tryLinuxShellPipelineSave(baseDir) {
|
|
|
38509
38847
|
if (process.platform !== "linux" && process.platform !== "freebsd") {
|
|
38510
38848
|
return null;
|
|
38511
38849
|
}
|
|
38512
|
-
const outPath =
|
|
38850
|
+
const outPath = path53.join(
|
|
38513
38851
|
baseDir,
|
|
38514
38852
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.tmp`
|
|
38515
38853
|
);
|
|
@@ -38531,11 +38869,11 @@ printf '%s' "$OUT"
|
|
|
38531
38869
|
maxBuffer: 4096
|
|
38532
38870
|
});
|
|
38533
38871
|
const written = String(stdout ?? "").trim();
|
|
38534
|
-
if (written !== outPath || !
|
|
38872
|
+
if (written !== outPath || !fs46.existsSync(outPath)) {
|
|
38535
38873
|
unlinkQuiet(outPath);
|
|
38536
38874
|
return null;
|
|
38537
38875
|
}
|
|
38538
|
-
const buf =
|
|
38876
|
+
const buf = fs46.readFileSync(outPath);
|
|
38539
38877
|
unlinkQuiet(outPath);
|
|
38540
38878
|
return writeBufferIfImage(baseDir, buf);
|
|
38541
38879
|
} catch {
|
|
@@ -38556,8 +38894,8 @@ async function tryNativeClipboardImage() {
|
|
|
38556
38894
|
}
|
|
38557
38895
|
try {
|
|
38558
38896
|
const result = readClipboardImageNative();
|
|
38559
|
-
if (
|
|
38560
|
-
const st =
|
|
38897
|
+
if (fs46.existsSync(result.path)) {
|
|
38898
|
+
const st = fs46.statSync(result.path);
|
|
38561
38899
|
if (st.size >= 80 && st.size <= CLIPBOARD_MAX_BYTES) {
|
|
38562
38900
|
return result.path;
|
|
38563
38901
|
}
|
|
@@ -38567,8 +38905,8 @@ async function tryNativeClipboardImage() {
|
|
|
38567
38905
|
return null;
|
|
38568
38906
|
}
|
|
38569
38907
|
async function readClipboardImageToTempFile() {
|
|
38570
|
-
const baseDir =
|
|
38571
|
-
|
|
38908
|
+
const baseDir = path53.join(os32.homedir(), ".cache", "bluma", "clipboard");
|
|
38909
|
+
fs46.mkdirSync(baseDir, { recursive: true });
|
|
38572
38910
|
const nativeResult = await tryNativeClipboardImage();
|
|
38573
38911
|
if (nativeResult) {
|
|
38574
38912
|
return nativeResult;
|
|
@@ -38586,7 +38924,7 @@ async function readClipboardImageToTempFile() {
|
|
|
38586
38924
|
}
|
|
38587
38925
|
}
|
|
38588
38926
|
if (process.platform === "win32") {
|
|
38589
|
-
const outFile =
|
|
38927
|
+
const outFile = path53.join(
|
|
38590
38928
|
baseDir,
|
|
38591
38929
|
`clip-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.png`
|
|
38592
38930
|
);
|
|
@@ -38627,7 +38965,7 @@ function expandLargePastePlaceholder(value, pending) {
|
|
|
38627
38965
|
}
|
|
38628
38966
|
|
|
38629
38967
|
// src/app/ui/components/InputPrompt.tsx
|
|
38630
|
-
import
|
|
38968
|
+
import fs47 from "fs";
|
|
38631
38969
|
import { Fragment as Fragment7, jsx as jsx79, jsxs as jsxs62 } from "react/jsx-runtime";
|
|
38632
38970
|
var persistedInputState = { text: "", cursorPosition: 0 };
|
|
38633
38971
|
var StaticCursor = () => /* @__PURE__ */ jsx79(Box_default, { flexDirection: "row", flexWrap: "nowrap", children: /* @__PURE__ */ jsx79(Text, { bold: true, color: BLUMA_TERMINAL.m3OnSurface, children: "\u2588 " }) });
|
|
@@ -38794,7 +39132,7 @@ var InputPrompt = memo16(({
|
|
|
38794
39132
|
return;
|
|
38795
39133
|
}
|
|
38796
39134
|
if (routeText.startsWith("/")) {
|
|
38797
|
-
const isFilePath = map.size > 0 &&
|
|
39135
|
+
const isFilePath = map.size > 0 && fs47.existsSync(routeText.split(/\s+/)[0]);
|
|
38798
39136
|
if (isFilePath) {
|
|
38799
39137
|
uiEventBus.emit("user_overlay", {
|
|
38800
39138
|
kind: "message",
|
|
@@ -40962,8 +41300,8 @@ var renderSettingsEditUsage = () => {
|
|
|
40962
41300
|
|
|
40963
41301
|
// src/app/agent/core/thread/thread_store.ts
|
|
40964
41302
|
init_bluma_app_dir();
|
|
40965
|
-
import
|
|
40966
|
-
import { promises as
|
|
41303
|
+
import path54 from "path";
|
|
41304
|
+
import { promises as fs48 } from "fs";
|
|
40967
41305
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
40968
41306
|
var INDEX_VERSION = 1;
|
|
40969
41307
|
var fileLocks2 = /* @__PURE__ */ new Map();
|
|
@@ -40988,9 +41326,9 @@ var ThreadStore = class {
|
|
|
40988
41326
|
packageVersion;
|
|
40989
41327
|
constructor() {
|
|
40990
41328
|
const appDir = getPreferredAppDir();
|
|
40991
|
-
this.threadsDir =
|
|
40992
|
-
this.archiveDir =
|
|
40993
|
-
this.indexPath =
|
|
41329
|
+
this.threadsDir = path54.join(appDir, "threads");
|
|
41330
|
+
this.archiveDir = path54.join(this.threadsDir, "archive");
|
|
41331
|
+
this.indexPath = path54.join(this.threadsDir, "index.json");
|
|
40994
41332
|
this.packageVersion = process.env.npm_package_version || "0.0.0";
|
|
40995
41333
|
}
|
|
40996
41334
|
// ==================== Inicialização ====================
|
|
@@ -40998,10 +41336,10 @@ var ThreadStore = class {
|
|
|
40998
41336
|
* Inicializa o diretório de threads
|
|
40999
41337
|
*/
|
|
41000
41338
|
async initialize() {
|
|
41001
|
-
await
|
|
41002
|
-
await
|
|
41339
|
+
await fs48.mkdir(this.threadsDir, { recursive: true });
|
|
41340
|
+
await fs48.mkdir(this.archiveDir, { recursive: true });
|
|
41003
41341
|
try {
|
|
41004
|
-
await
|
|
41342
|
+
await fs48.access(this.indexPath);
|
|
41005
41343
|
} catch {
|
|
41006
41344
|
await this.saveIndex({ version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() });
|
|
41007
41345
|
}
|
|
@@ -41030,7 +41368,7 @@ var ThreadStore = class {
|
|
|
41030
41368
|
async loadIndex() {
|
|
41031
41369
|
return withFileLock2(this.indexPath, async () => {
|
|
41032
41370
|
try {
|
|
41033
|
-
const content = await
|
|
41371
|
+
const content = await fs48.readFile(this.indexPath, "utf-8");
|
|
41034
41372
|
return JSON.parse(content);
|
|
41035
41373
|
} catch {
|
|
41036
41374
|
return { version: INDEX_VERSION, threads: [], lastUpdated: (/* @__PURE__ */ new Date()).toISOString() };
|
|
@@ -41041,8 +41379,8 @@ var ThreadStore = class {
|
|
|
41041
41379
|
return withFileLock2(this.indexPath, async () => {
|
|
41042
41380
|
index.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
41043
41381
|
const tempPath = `${this.indexPath}.${Date.now()}.tmp`;
|
|
41044
|
-
await
|
|
41045
|
-
await
|
|
41382
|
+
await fs48.writeFile(tempPath, JSON.stringify(index, null, 2), "utf-8");
|
|
41383
|
+
await fs48.rename(tempPath, this.indexPath);
|
|
41046
41384
|
});
|
|
41047
41385
|
}
|
|
41048
41386
|
// ==================== Git Info ====================
|
|
@@ -41112,7 +41450,7 @@ var ThreadStore = class {
|
|
|
41112
41450
|
messages: params.initialMessages || []
|
|
41113
41451
|
};
|
|
41114
41452
|
const historyPath = this.buildDatedThreadHistoryPath(threadId);
|
|
41115
|
-
await
|
|
41453
|
+
await fs48.mkdir(path54.dirname(historyPath), { recursive: true });
|
|
41116
41454
|
await this.saveHistoryAtPath(historyPath, history);
|
|
41117
41455
|
const index = await this.loadIndex();
|
|
41118
41456
|
index.threads.unshift({
|
|
@@ -41167,7 +41505,7 @@ var ThreadStore = class {
|
|
|
41167
41505
|
compressedSliceCount: source.history.compressedSliceCount
|
|
41168
41506
|
};
|
|
41169
41507
|
const historyPath = this.buildDatedThreadHistoryPath(newThreadId);
|
|
41170
|
-
await
|
|
41508
|
+
await fs48.mkdir(path54.dirname(historyPath), { recursive: true });
|
|
41171
41509
|
await this.saveHistoryAtPath(historyPath, history);
|
|
41172
41510
|
const index = await this.loadIndex();
|
|
41173
41511
|
index.threads.unshift({
|
|
@@ -41240,9 +41578,9 @@ var ThreadStore = class {
|
|
|
41240
41578
|
const entry = index.threads[entryIndex];
|
|
41241
41579
|
if (entry.status === "archived") return true;
|
|
41242
41580
|
const oldPath = entry.historyPath || this.getLegacyHistoryPath(threadId);
|
|
41243
|
-
const newPath =
|
|
41581
|
+
const newPath = path54.join(this.archiveDir, `${threadId}.jsonl`);
|
|
41244
41582
|
try {
|
|
41245
|
-
await
|
|
41583
|
+
await fs48.rename(oldPath, newPath);
|
|
41246
41584
|
} catch (e) {
|
|
41247
41585
|
if (e.code !== "ENOENT") throw e;
|
|
41248
41586
|
}
|
|
@@ -41263,11 +41601,11 @@ var ThreadStore = class {
|
|
|
41263
41601
|
if (entryIndex === -1) return false;
|
|
41264
41602
|
const entry = index.threads[entryIndex];
|
|
41265
41603
|
if (entry.status === "active") return true;
|
|
41266
|
-
const oldPath =
|
|
41604
|
+
const oldPath = path54.join(this.archiveDir, `${threadId}.jsonl`);
|
|
41267
41605
|
const newPath = this.buildDatedThreadHistoryPath(threadId);
|
|
41268
|
-
await
|
|
41606
|
+
await fs48.mkdir(path54.dirname(newPath), { recursive: true });
|
|
41269
41607
|
try {
|
|
41270
|
-
await
|
|
41608
|
+
await fs48.rename(oldPath, newPath);
|
|
41271
41609
|
} catch (e) {
|
|
41272
41610
|
if (e.code !== "ENOENT") throw e;
|
|
41273
41611
|
}
|
|
@@ -41288,7 +41626,7 @@ var ThreadStore = class {
|
|
|
41288
41626
|
if (entryIndex === -1) return false;
|
|
41289
41627
|
const entry = index.threads[entryIndex];
|
|
41290
41628
|
try {
|
|
41291
|
-
await
|
|
41629
|
+
await fs48.unlink(entry.historyPath);
|
|
41292
41630
|
} catch {
|
|
41293
41631
|
}
|
|
41294
41632
|
index.threads.splice(entryIndex, 1);
|
|
@@ -41298,28 +41636,28 @@ var ThreadStore = class {
|
|
|
41298
41636
|
}
|
|
41299
41637
|
// ==================== Histórico ====================
|
|
41300
41638
|
getLegacyHistoryPath(threadId) {
|
|
41301
|
-
return
|
|
41639
|
+
return path54.join(this.threadsDir, `${threadId}.jsonl`);
|
|
41302
41640
|
}
|
|
41303
41641
|
/** ~/.bluma/threads/YYYY/MM/DD/<threadId>.jsonl (data local de criação). */
|
|
41304
41642
|
buildDatedThreadHistoryPath(threadId, at = /* @__PURE__ */ new Date()) {
|
|
41305
41643
|
const y = String(at.getFullYear());
|
|
41306
41644
|
const mo = String(at.getMonth() + 1).padStart(2, "0");
|
|
41307
41645
|
const d = String(at.getDate()).padStart(2, "0");
|
|
41308
|
-
return
|
|
41646
|
+
return path54.join(this.threadsDir, y, mo, d, `${threadId}.jsonl`);
|
|
41309
41647
|
}
|
|
41310
41648
|
async resolveHistoryPath(threadId) {
|
|
41311
41649
|
const index = await this.loadIndex();
|
|
41312
41650
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
41313
41651
|
if (entry?.historyPath) {
|
|
41314
41652
|
try {
|
|
41315
|
-
await
|
|
41653
|
+
await fs48.access(entry.historyPath);
|
|
41316
41654
|
return entry.historyPath;
|
|
41317
41655
|
} catch {
|
|
41318
41656
|
}
|
|
41319
41657
|
}
|
|
41320
41658
|
const legacy = this.getLegacyHistoryPath(threadId);
|
|
41321
41659
|
try {
|
|
41322
|
-
await
|
|
41660
|
+
await fs48.access(legacy);
|
|
41323
41661
|
return legacy;
|
|
41324
41662
|
} catch {
|
|
41325
41663
|
return entry?.historyPath ?? legacy;
|
|
@@ -41336,9 +41674,9 @@ var ThreadStore = class {
|
|
|
41336
41674
|
for (const msg of history.messages) {
|
|
41337
41675
|
lines.push(JSON.stringify({ type: "message", ...msg }));
|
|
41338
41676
|
}
|
|
41339
|
-
await
|
|
41677
|
+
await fs48.mkdir(path54.dirname(historyPath), { recursive: true }).catch(() => {
|
|
41340
41678
|
});
|
|
41341
|
-
await
|
|
41679
|
+
await fs48.writeFile(historyPath, lines.join("\n") + "\n", "utf-8");
|
|
41342
41680
|
}
|
|
41343
41681
|
/**
|
|
41344
41682
|
* Guarda o histórico de uma thread
|
|
@@ -41360,7 +41698,7 @@ var ThreadStore = class {
|
|
|
41360
41698
|
].filter((p, i, arr) => Boolean(p) && arr.indexOf(p) === i);
|
|
41361
41699
|
for (const historyPath of pathsToTry) {
|
|
41362
41700
|
try {
|
|
41363
|
-
const content = await
|
|
41701
|
+
const content = await fs48.readFile(historyPath, "utf-8");
|
|
41364
41702
|
const lines = content.split("\n").filter(Boolean);
|
|
41365
41703
|
const history = {
|
|
41366
41704
|
threadId,
|
|
@@ -41395,7 +41733,7 @@ var ThreadStore = class {
|
|
|
41395
41733
|
const entry = index.threads.find((t) => t.threadId === threadId);
|
|
41396
41734
|
if (!entry) throw new Error(`Thread not found: ${threadId}`);
|
|
41397
41735
|
const lines = messages.map((msg) => JSON.stringify({ type: "message", ...msg }));
|
|
41398
|
-
await
|
|
41736
|
+
await fs48.appendFile(entry.historyPath, lines.join("\n") + "\n", "utf-8");
|
|
41399
41737
|
entry.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
41400
41738
|
await this.saveIndex(index);
|
|
41401
41739
|
}
|
|
@@ -43832,16 +44170,16 @@ import latestVersion from "latest-version";
|
|
|
43832
44170
|
import semverGt from "semver/functions/gt.js";
|
|
43833
44171
|
import semverValid from "semver/functions/valid.js";
|
|
43834
44172
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
43835
|
-
import
|
|
43836
|
-
import
|
|
44173
|
+
import path56 from "path";
|
|
44174
|
+
import fs50 from "fs";
|
|
43837
44175
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
43838
44176
|
function findBlumaPackageJson(startDir) {
|
|
43839
44177
|
let dir = startDir;
|
|
43840
44178
|
for (let i = 0; i < 12; i++) {
|
|
43841
|
-
const candidate =
|
|
43842
|
-
if (
|
|
44179
|
+
const candidate = path56.join(dir, "package.json");
|
|
44180
|
+
if (fs50.existsSync(candidate)) {
|
|
43843
44181
|
try {
|
|
43844
|
-
const raw =
|
|
44182
|
+
const raw = fs50.readFileSync(candidate, "utf8");
|
|
43845
44183
|
const parsed = JSON.parse(raw);
|
|
43846
44184
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
43847
44185
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -43849,7 +44187,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
43849
44187
|
} catch {
|
|
43850
44188
|
}
|
|
43851
44189
|
}
|
|
43852
|
-
const parent =
|
|
44190
|
+
const parent = path56.dirname(dir);
|
|
43853
44191
|
if (parent === dir) break;
|
|
43854
44192
|
dir = parent;
|
|
43855
44193
|
}
|
|
@@ -43858,13 +44196,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
43858
44196
|
function resolveInstalledBlumaPackage() {
|
|
43859
44197
|
const tried = /* @__PURE__ */ new Set();
|
|
43860
44198
|
const tryFrom = (dir) => {
|
|
43861
|
-
const abs =
|
|
44199
|
+
const abs = path56.resolve(dir);
|
|
43862
44200
|
if (tried.has(abs)) return null;
|
|
43863
44201
|
tried.add(abs);
|
|
43864
44202
|
return findBlumaPackageJson(abs);
|
|
43865
44203
|
};
|
|
43866
44204
|
try {
|
|
43867
|
-
const fromBundle = tryFrom(
|
|
44205
|
+
const fromBundle = tryFrom(path56.dirname(fileURLToPath7(import.meta.url)));
|
|
43868
44206
|
if (fromBundle) return fromBundle;
|
|
43869
44207
|
} catch {
|
|
43870
44208
|
}
|
|
@@ -43872,12 +44210,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
43872
44210
|
if (argv1 && !argv1.startsWith("-")) {
|
|
43873
44211
|
try {
|
|
43874
44212
|
let resolved = argv1;
|
|
43875
|
-
if (
|
|
43876
|
-
resolved =
|
|
44213
|
+
if (path56.isAbsolute(argv1) && fs50.existsSync(argv1)) {
|
|
44214
|
+
resolved = fs50.realpathSync(argv1);
|
|
43877
44215
|
} else {
|
|
43878
|
-
resolved =
|
|
44216
|
+
resolved = path56.resolve(process.cwd(), argv1);
|
|
43879
44217
|
}
|
|
43880
|
-
const fromArgv = tryFrom(
|
|
44218
|
+
const fromArgv = tryFrom(path56.dirname(resolved));
|
|
43881
44219
|
if (fromArgv) return fromArgv;
|
|
43882
44220
|
} catch {
|
|
43883
44221
|
}
|
|
@@ -44696,16 +45034,16 @@ function usePlanMode() {
|
|
|
44696
45034
|
|
|
44697
45035
|
// src/app/hooks/useAgentMode.ts
|
|
44698
45036
|
import { useState as useState22, useEffect as useEffect21, useCallback as useCallback9 } from "react";
|
|
44699
|
-
import * as
|
|
44700
|
-
import * as
|
|
45037
|
+
import * as fs51 from "fs";
|
|
45038
|
+
import * as path57 from "path";
|
|
44701
45039
|
import { homedir as homedir4 } from "os";
|
|
44702
|
-
var SETTINGS_PATH =
|
|
45040
|
+
var SETTINGS_PATH = path57.join(homedir4(), ".bluma", "settings.json");
|
|
44703
45041
|
function readAgentModeFromFile() {
|
|
44704
45042
|
try {
|
|
44705
|
-
if (!
|
|
45043
|
+
if (!fs51.existsSync(SETTINGS_PATH)) {
|
|
44706
45044
|
return "default";
|
|
44707
45045
|
}
|
|
44708
|
-
const content =
|
|
45046
|
+
const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
|
|
44709
45047
|
const settings = JSON.parse(content);
|
|
44710
45048
|
return settings.agentMode || "default";
|
|
44711
45049
|
} catch (error) {
|
|
@@ -44724,16 +45062,16 @@ function useAgentMode() {
|
|
|
44724
45062
|
}, []);
|
|
44725
45063
|
const updateAgentMode = useCallback9((mode) => {
|
|
44726
45064
|
try {
|
|
44727
|
-
if (!
|
|
44728
|
-
|
|
45065
|
+
if (!fs51.existsSync(SETTINGS_PATH)) {
|
|
45066
|
+
fs51.mkdirSync(path57.dirname(SETTINGS_PATH), { recursive: true });
|
|
44729
45067
|
}
|
|
44730
45068
|
let settings = {};
|
|
44731
|
-
if (
|
|
44732
|
-
const content =
|
|
45069
|
+
if (fs51.existsSync(SETTINGS_PATH)) {
|
|
45070
|
+
const content = fs51.readFileSync(SETTINGS_PATH, "utf-8");
|
|
44733
45071
|
settings = JSON.parse(content);
|
|
44734
45072
|
}
|
|
44735
45073
|
settings.agentMode = mode;
|
|
44736
|
-
|
|
45074
|
+
fs51.writeFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2), "utf-8");
|
|
44737
45075
|
setAgentMode(mode);
|
|
44738
45076
|
} catch (error) {
|
|
44739
45077
|
console.error("Failed to update agent mode:", error);
|
|
@@ -45997,10 +46335,10 @@ import { memo as memo26, useCallback as useCallback11, useEffect as useEffect23,
|
|
|
45997
46335
|
|
|
45998
46336
|
// src/app/agent/session_manager/session_resume_browser.ts
|
|
45999
46337
|
init_bluma_app_dir();
|
|
46000
|
-
import
|
|
46001
|
-
import { promises as
|
|
46338
|
+
import path58 from "path";
|
|
46339
|
+
import { promises as fs52 } from "fs";
|
|
46002
46340
|
function getSessionsRoot() {
|
|
46003
|
-
return
|
|
46341
|
+
return path58.join(getPreferredAppDir(), "sessions");
|
|
46004
46342
|
}
|
|
46005
46343
|
function normalizeSessionsCwd(cwd2) {
|
|
46006
46344
|
return cwd2.replace(/\\/g, "/").split("/").filter((p) => p && p !== "." && p !== "..").join("/");
|
|
@@ -46021,9 +46359,9 @@ async function sessionEntryFromFile(absPath, sessionId) {
|
|
|
46021
46359
|
let preview = "(no messages)";
|
|
46022
46360
|
let lastActivityMs = Date.now();
|
|
46023
46361
|
try {
|
|
46024
|
-
const st = await
|
|
46362
|
+
const st = await fs52.stat(absPath);
|
|
46025
46363
|
lastActivityMs = st.mtimeMs;
|
|
46026
|
-
const raw = await
|
|
46364
|
+
const raw = await fs52.readFile(absPath, "utf-8");
|
|
46027
46365
|
const data = JSON.parse(raw);
|
|
46028
46366
|
preview = previewFromHistory(data.conversation_history);
|
|
46029
46367
|
const iso = data.last_updated || data.created_at;
|
|
@@ -46051,15 +46389,15 @@ function compareDirNames(a, b) {
|
|
|
46051
46389
|
async function listSessionBrowserEntries(cwdRel) {
|
|
46052
46390
|
const cwd2 = normalizeSessionsCwd(cwdRel);
|
|
46053
46391
|
const root = getSessionsRoot();
|
|
46054
|
-
const absDir = cwd2 ?
|
|
46392
|
+
const absDir = cwd2 ? path58.join(root, ...cwd2.split("/")) : root;
|
|
46055
46393
|
const out = [];
|
|
46056
46394
|
if (cwd2) {
|
|
46057
46395
|
out.push({ kind: "up", label: ".." });
|
|
46058
46396
|
}
|
|
46059
46397
|
let dirents;
|
|
46060
46398
|
try {
|
|
46061
|
-
await
|
|
46062
|
-
dirents = await
|
|
46399
|
+
await fs52.mkdir(absDir, { recursive: true });
|
|
46400
|
+
dirents = await fs52.readdir(absDir, { withFileTypes: true });
|
|
46063
46401
|
} catch {
|
|
46064
46402
|
return out;
|
|
46065
46403
|
}
|
|
@@ -46068,7 +46406,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
46068
46406
|
for (const e of dirents) {
|
|
46069
46407
|
const name = String(e.name);
|
|
46070
46408
|
if (name.startsWith(".")) continue;
|
|
46071
|
-
const full =
|
|
46409
|
+
const full = path58.join(absDir, name);
|
|
46072
46410
|
if (e.isDirectory()) {
|
|
46073
46411
|
dirNames.push(name);
|
|
46074
46412
|
} else if (e.isFile() && name.endsWith(".json") && !name.includes(".tmp")) {
|
|
@@ -46078,7 +46416,7 @@ async function listSessionBrowserEntries(cwdRel) {
|
|
|
46078
46416
|
dirNames.sort(compareDirNames);
|
|
46079
46417
|
const sessions = [];
|
|
46080
46418
|
for (const { name, full } of sessionFiles) {
|
|
46081
|
-
const sessionId =
|
|
46419
|
+
const sessionId = path58.basename(name, ".json");
|
|
46082
46420
|
sessions.push(await sessionEntryFromFile(full, sessionId));
|
|
46083
46421
|
}
|
|
46084
46422
|
sessions.sort((a, b) => {
|
|
@@ -46443,9 +46781,9 @@ async function runAgentMode() {
|
|
|
46443
46781
|
try {
|
|
46444
46782
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
46445
46783
|
const filePath = args[inputFileIndex + 1];
|
|
46446
|
-
rawPayload =
|
|
46784
|
+
rawPayload = fs53.readFileSync(filePath, "utf-8");
|
|
46447
46785
|
} else {
|
|
46448
|
-
rawPayload =
|
|
46786
|
+
rawPayload = fs53.readFileSync(0, "utf-8");
|
|
46449
46787
|
}
|
|
46450
46788
|
} catch (err) {
|
|
46451
46789
|
writeAgentEvent(registrySessionId, {
|
|
@@ -46497,6 +46835,12 @@ async function runAgentMode() {
|
|
|
46497
46835
|
if (envelopeUserRequest.trim()) {
|
|
46498
46836
|
process.env.BLUMA_USER_REQUEST = envelopeUserRequest.trim();
|
|
46499
46837
|
}
|
|
46838
|
+
if (envelope.from_agent?.trim()) {
|
|
46839
|
+
process.env.BLUMA_FROM_AGENT = envelope.from_agent.trim();
|
|
46840
|
+
}
|
|
46841
|
+
if (envelope.action?.trim()) {
|
|
46842
|
+
process.env.BLUMA_ACTION = envelope.action.trim();
|
|
46843
|
+
}
|
|
46500
46844
|
const eventBus = new EventEmitter7();
|
|
46501
46845
|
const sessionId = registrySessionId || envelope.session_id || envelope.message_id || uuidv412();
|
|
46502
46846
|
process.env.BLUMA_SESSION_ID = sessionId;
|
|
@@ -46686,9 +47030,9 @@ async function runAgentMode() {
|
|
|
46686
47030
|
}
|
|
46687
47031
|
function readCliPackageVersion() {
|
|
46688
47032
|
try {
|
|
46689
|
-
const base =
|
|
46690
|
-
const pkgPath =
|
|
46691
|
-
const j = JSON.parse(
|
|
47033
|
+
const base = path59.dirname(fileURLToPath8(import.meta.url));
|
|
47034
|
+
const pkgPath = path59.join(base, "..", "package.json");
|
|
47035
|
+
const j = JSON.parse(fs53.readFileSync(pkgPath, "utf8"));
|
|
46692
47036
|
return String(j.version || "0.0.0");
|
|
46693
47037
|
} catch {
|
|
46694
47038
|
return "0.0.0";
|
|
@@ -46814,7 +47158,7 @@ function startBackgroundAgent() {
|
|
|
46814
47158
|
process.exit(1);
|
|
46815
47159
|
}
|
|
46816
47160
|
const filePath = args[inputFileIndex + 1];
|
|
46817
|
-
const rawPayload =
|
|
47161
|
+
const rawPayload = fs53.readFileSync(filePath, "utf-8");
|
|
46818
47162
|
const envelope = JSON.parse(rawPayload);
|
|
46819
47163
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
46820
47164
|
registerSession({
|