@nomad-e/bluma-cli 0.1.71 → 0.1.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js
CHANGED
|
@@ -199,7 +199,7 @@ var init_permission_rules = __esm({
|
|
|
199
199
|
matchesPattern(pattern, value) {
|
|
200
200
|
if (pattern === "*") return true;
|
|
201
201
|
if (pattern.includes("*")) {
|
|
202
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "
|
|
202
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, ".*");
|
|
203
203
|
const regex = new RegExp("^" + escaped + "$");
|
|
204
204
|
return regex.test(value);
|
|
205
205
|
}
|
|
@@ -319,13 +319,31 @@ var init_sandbox_policy = __esm({
|
|
|
319
319
|
init_runtime_config();
|
|
320
320
|
init_permission_rules();
|
|
321
321
|
BLOCKED_COMMAND_PATTERNS = [
|
|
322
|
-
|
|
322
|
+
{ pattern: /\bsudo\b/, reason: "Privilege escalation is not allowed." },
|
|
323
|
+
{ pattern: /\bsu\b\s/, reason: "User switching is not allowed." },
|
|
324
|
+
{ pattern: /\brm\s+-rf\s+\/\b/, reason: "Deleting root filesystem is blocked." },
|
|
325
|
+
{ pattern: /\bcurl\b.*\|\s*(bash|sh|zsh)/i, reason: "Pipe-to-shell execution is blocked." },
|
|
326
|
+
{ pattern: /\bwget\b.*\|\s*(bash|sh|zsh)/i, reason: "Pipe-to-shell execution is blocked." },
|
|
327
|
+
{ pattern: /\beval\b\s*\(/, reason: "Eval execution is blocked." },
|
|
328
|
+
{ pattern: /\bmkfs\b/, reason: "Filesystem formatting is blocked." },
|
|
329
|
+
{ pattern: /\bdd\s+of=/, reason: "Raw disk write is blocked." }
|
|
323
330
|
];
|
|
324
331
|
HIGH_RISK_COMMAND_PATTERNS = [
|
|
325
|
-
|
|
332
|
+
/\brm\s+-rf\b/,
|
|
333
|
+
/\bchmod\s+-?777\b/,
|
|
334
|
+
/\bchown\s/,
|
|
335
|
+
/\bmkfs\b/,
|
|
336
|
+
/\bformat\b/
|
|
326
337
|
];
|
|
327
338
|
MODERATE_RISK_COMMAND_PATTERNS = [
|
|
328
|
-
|
|
339
|
+
/\bnpm\s+install\b/,
|
|
340
|
+
/\bnpm\s+update\b/,
|
|
341
|
+
/\byarn\s+install\b/,
|
|
342
|
+
/\byarn\s+upgrade\b/,
|
|
343
|
+
/\bpnpm\s+install\b/,
|
|
344
|
+
/\bpip\s+install\b/,
|
|
345
|
+
/\bapt-get\s+install\b/,
|
|
346
|
+
/\bbrew\s+install\b/
|
|
329
347
|
];
|
|
330
348
|
}
|
|
331
349
|
});
|
|
@@ -1737,8 +1755,8 @@ var init_themes = __esm({
|
|
|
1737
1755
|
import React19 from "react";
|
|
1738
1756
|
import { render } from "ink";
|
|
1739
1757
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
1740
|
-
import
|
|
1741
|
-
import
|
|
1758
|
+
import fs39 from "fs";
|
|
1759
|
+
import path44 from "path";
|
|
1742
1760
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
1743
1761
|
import { spawn as spawn6 } from "child_process";
|
|
1744
1762
|
import { v4 as uuidv412 } from "uuid";
|
|
@@ -4438,12 +4456,12 @@ function EditToolDiffPanel({
|
|
|
4438
4456
|
maxHeight = EDIT_DIFF_PREVIEW_MAX_LINES,
|
|
4439
4457
|
fallbackSnippet
|
|
4440
4458
|
}) {
|
|
4441
|
-
const
|
|
4459
|
+
const path45 = filePath.trim() || "unknown file";
|
|
4442
4460
|
const diff = diffText?.trim() ?? "";
|
|
4443
4461
|
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
4444
4462
|
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", flexWrap: "wrap", children: /* @__PURE__ */ jsxs5(Text5, { color: isNewFile ? BLUMA_TERMINAL.success : void 0, children: [
|
|
4445
4463
|
isNewFile ? "Created " : "Wrote to ",
|
|
4446
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, children:
|
|
4464
|
+
/* @__PURE__ */ jsx5(Text5, { bold: true, children: path45 })
|
|
4447
4465
|
] }) }),
|
|
4448
4466
|
description ? /* @__PURE__ */ jsx5(Text5, { dimColor: true, wrap: "wrap", children: description }) : null,
|
|
4449
4467
|
diff.length > 0 ? /* @__PURE__ */ jsx5(Box5, { marginTop: 0, children: /* @__PURE__ */ jsx5(SimpleDiff, { text: diff, maxHeight, frame: true }) }) : fallbackSnippet ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 0, children: [
|
|
@@ -4772,7 +4790,7 @@ var renderFindByName = ({ args }) => {
|
|
|
4772
4790
|
var renderGrepSearch = ({ args }) => {
|
|
4773
4791
|
const parsed = parseArgs(args);
|
|
4774
4792
|
const query = parsed.query || "";
|
|
4775
|
-
const
|
|
4793
|
+
const path45 = parsed.path || ".";
|
|
4776
4794
|
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", flexWrap: "wrap", children: [
|
|
4777
4795
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.muted, children: [
|
|
4778
4796
|
'"',
|
|
@@ -4781,7 +4799,7 @@ var renderGrepSearch = ({ args }) => {
|
|
|
4781
4799
|
] }),
|
|
4782
4800
|
/* @__PURE__ */ jsxs8(Text8, { color: BLUMA_TERMINAL.m3OnSurface, bold: true, children: [
|
|
4783
4801
|
" ",
|
|
4784
|
-
|
|
4802
|
+
path45
|
|
4785
4803
|
] })
|
|
4786
4804
|
] });
|
|
4787
4805
|
};
|
|
@@ -5207,12 +5225,12 @@ var ConfirmationPrompt = memo4(ConfirmationPromptComponent);
|
|
|
5207
5225
|
|
|
5208
5226
|
// src/app/agent/agent.ts
|
|
5209
5227
|
import * as dotenv from "dotenv";
|
|
5210
|
-
import
|
|
5228
|
+
import path42 from "path";
|
|
5211
5229
|
import os28 from "os";
|
|
5212
5230
|
|
|
5213
5231
|
// src/app/agent/tool_invoker.ts
|
|
5214
|
-
import { promises as
|
|
5215
|
-
import
|
|
5232
|
+
import { promises as fs26 } from "fs";
|
|
5233
|
+
import path28 from "path";
|
|
5216
5234
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5217
5235
|
|
|
5218
5236
|
// src/app/agent/tools/natives/edit.ts
|
|
@@ -10308,19 +10326,86 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
|
10308
10326
|
}
|
|
10309
10327
|
const data = response.data || {};
|
|
10310
10328
|
const resolvedAppId = appId || data.appId;
|
|
10329
|
+
const appContext = data.appContext ? {
|
|
10330
|
+
appId: String(data.appContext.appId || resolvedAppId || "").trim(),
|
|
10331
|
+
tenantId: data.appContext.tenantId ?? null,
|
|
10332
|
+
projectId: data.appContext.projectId ?? null,
|
|
10333
|
+
workspaceId: data.appContext.workspaceId ?? null,
|
|
10334
|
+
revisionId: data.appContext.revisionId ?? null,
|
|
10335
|
+
deploymentId: data.appContext.deploymentId ?? null,
|
|
10336
|
+
buildJobId: data.appContext.buildJobId ?? null,
|
|
10337
|
+
manifestPath: data.appContext.manifestPath ?? "factorai.sh.json",
|
|
10338
|
+
manifestFile: data.appContext.manifestFile ?? "factorai.sh.json",
|
|
10339
|
+
appUrl: data.appContext.appUrl ?? `${severinoUrl.replace(/\/$/, "")}/app/${resolvedAppId}`
|
|
10340
|
+
} : {
|
|
10341
|
+
appId: String(resolvedAppId || "").trim(),
|
|
10342
|
+
tenantId: data.tenantId ?? null,
|
|
10343
|
+
projectId: data.projectId ?? null,
|
|
10344
|
+
workspaceId: data.workspaceId ?? null,
|
|
10345
|
+
revisionId: data.revisionId ?? null,
|
|
10346
|
+
deploymentId: data.deploymentId ?? null,
|
|
10347
|
+
buildJobId: data.buildJobId ?? null,
|
|
10348
|
+
manifestPath: "factorai.sh.json",
|
|
10349
|
+
manifestFile: "factorai.sh.json",
|
|
10350
|
+
appUrl: `${severinoUrl.replace(/\/$/, "")}/app/${resolvedAppId}`
|
|
10351
|
+
};
|
|
10311
10352
|
return {
|
|
10312
10353
|
success: true,
|
|
10313
10354
|
appId: resolvedAppId,
|
|
10314
10355
|
name: data.name,
|
|
10315
10356
|
status: data.status || "building",
|
|
10316
|
-
url: severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
|
|
10357
|
+
url: appContext.appUrl || severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
|
|
10317
10358
|
message: data.message || "Deploy iniciado",
|
|
10318
|
-
isRedeploy: !!appId
|
|
10359
|
+
isRedeploy: !!appId,
|
|
10360
|
+
appContext
|
|
10319
10361
|
};
|
|
10320
10362
|
} catch (parseError) {
|
|
10321
10363
|
throw new Error(`Failed to parse response: ${parseError.message}`);
|
|
10322
10364
|
}
|
|
10323
10365
|
}
|
|
10366
|
+
function buildFactorAiManifest(appContext, deployResult, appName) {
|
|
10367
|
+
return {
|
|
10368
|
+
version: 1,
|
|
10369
|
+
manifestFile: "factorai.sh.json",
|
|
10370
|
+
manifestPath: "factorai.sh.json",
|
|
10371
|
+
appContext: {
|
|
10372
|
+
appId: appContext.appId,
|
|
10373
|
+
tenantId: appContext.tenantId ?? null,
|
|
10374
|
+
projectId: appContext.projectId ?? null,
|
|
10375
|
+
workspaceId: appContext.workspaceId ?? null,
|
|
10376
|
+
revisionId: appContext.revisionId ?? null,
|
|
10377
|
+
deploymentId: appContext.deploymentId ?? null,
|
|
10378
|
+
buildJobId: appContext.buildJobId ?? null,
|
|
10379
|
+
manifestPath: "factorai.sh.json",
|
|
10380
|
+
manifestFile: "factorai.sh.json",
|
|
10381
|
+
appUrl: appContext.appUrl ?? deployResult.url ?? null
|
|
10382
|
+
},
|
|
10383
|
+
app: {
|
|
10384
|
+
name: appName,
|
|
10385
|
+
status: deployResult.status || "building",
|
|
10386
|
+
isRedeploy: deployResult.isRedeploy ?? false,
|
|
10387
|
+
url: deployResult.url || null,
|
|
10388
|
+
message: deployResult.message || null
|
|
10389
|
+
},
|
|
10390
|
+
agent: {
|
|
10391
|
+
provider: "bluma",
|
|
10392
|
+
mode: "tool-first",
|
|
10393
|
+
instructions: [
|
|
10394
|
+
"Read factorai.sh.json as the source of truth before editing files.",
|
|
10395
|
+
"Prefer incremental changes and preserve the existing deployment context.",
|
|
10396
|
+
"After edits, use the redeploy tool instead of rebuilding from scratch."
|
|
10397
|
+
]
|
|
10398
|
+
},
|
|
10399
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
10400
|
+
};
|
|
10401
|
+
}
|
|
10402
|
+
async function writeFactorAiManifest(projectDir, appContext, deployResult, appName) {
|
|
10403
|
+
const manifest = buildFactorAiManifest(appContext, deployResult, appName);
|
|
10404
|
+
const manifestPath = path26.join(projectDir, "factorai.sh.json");
|
|
10405
|
+
await fs24.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
10406
|
+
`, "utf-8");
|
|
10407
|
+
return manifest;
|
|
10408
|
+
}
|
|
10324
10409
|
async function deployApp(args) {
|
|
10325
10410
|
const envSeverinoUrl = process.env.SEVERINO_URL || "http://localhost:3000";
|
|
10326
10411
|
const envApiKey = process.env.SEVERINO_API_KEY || void 0;
|
|
@@ -10390,6 +10475,16 @@ async function deployApp(args) {
|
|
|
10390
10475
|
console.warn("[deploy-app] Cleanup warning:", e);
|
|
10391
10476
|
}
|
|
10392
10477
|
if (deployResult.success) {
|
|
10478
|
+
if (deployResult.appContext) {
|
|
10479
|
+
const manifest = await writeFactorAiManifest(
|
|
10480
|
+
resolvedProjectDir,
|
|
10481
|
+
deployResult.appContext,
|
|
10482
|
+
deployResult,
|
|
10483
|
+
appName
|
|
10484
|
+
);
|
|
10485
|
+
deployResult.factoraiManifest = manifest;
|
|
10486
|
+
deployResult.factoraiManifestPath = path26.join(resolvedProjectDir, "factorai.sh.json");
|
|
10487
|
+
}
|
|
10393
10488
|
console.log(`[deploy-app] Deploy iniciado: ${deployResult.appId}`);
|
|
10394
10489
|
}
|
|
10395
10490
|
return deployResult;
|
|
@@ -10402,8 +10497,389 @@ async function deployApp(args) {
|
|
|
10402
10497
|
}
|
|
10403
10498
|
}
|
|
10404
10499
|
|
|
10500
|
+
// src/app/agent/runtime/factorai_context.ts
|
|
10501
|
+
import fs25 from "fs";
|
|
10502
|
+
import path27 from "path";
|
|
10503
|
+
function normalizeContext(raw) {
|
|
10504
|
+
if (!raw || typeof raw.appId !== "string" || !raw.appId.trim()) {
|
|
10505
|
+
return null;
|
|
10506
|
+
}
|
|
10507
|
+
const appId = raw.appId.trim();
|
|
10508
|
+
return {
|
|
10509
|
+
appId,
|
|
10510
|
+
tenantId: typeof raw.tenantId === "string" ? raw.tenantId : null,
|
|
10511
|
+
projectId: typeof raw.projectId === "string" ? raw.projectId : null,
|
|
10512
|
+
workspaceId: typeof raw.workspaceId === "string" ? raw.workspaceId : null,
|
|
10513
|
+
revisionId: typeof raw.revisionId === "string" ? raw.revisionId : null,
|
|
10514
|
+
deploymentId: typeof raw.deploymentId === "string" ? raw.deploymentId : null,
|
|
10515
|
+
buildJobId: typeof raw.buildJobId === "string" ? raw.buildJobId : null,
|
|
10516
|
+
manifestPath: typeof raw.manifestPath === "string" ? raw.manifestPath : null,
|
|
10517
|
+
manifestFile: typeof raw.manifestFile === "string" ? raw.manifestFile : null,
|
|
10518
|
+
appUrl: typeof raw.appUrl === "string" ? raw.appUrl : null
|
|
10519
|
+
};
|
|
10520
|
+
}
|
|
10521
|
+
function readJsonFile(filePath) {
|
|
10522
|
+
try {
|
|
10523
|
+
if (!fs25.existsSync(filePath)) {
|
|
10524
|
+
return null;
|
|
10525
|
+
}
|
|
10526
|
+
const parsed = JSON.parse(fs25.readFileSync(filePath, "utf8"));
|
|
10527
|
+
if (parsed && typeof parsed === "object") {
|
|
10528
|
+
return parsed.appContext && typeof parsed.appContext === "object" ? parsed.appContext : parsed;
|
|
10529
|
+
}
|
|
10530
|
+
} catch {
|
|
10531
|
+
return null;
|
|
10532
|
+
}
|
|
10533
|
+
return null;
|
|
10534
|
+
}
|
|
10535
|
+
function readFactorAiWorkspaceManifest(projectDir = process.cwd()) {
|
|
10536
|
+
const manifestPath = path27.join(projectDir, "factorai.sh.json");
|
|
10537
|
+
try {
|
|
10538
|
+
if (!fs25.existsSync(manifestPath)) {
|
|
10539
|
+
return null;
|
|
10540
|
+
}
|
|
10541
|
+
const parsed = JSON.parse(fs25.readFileSync(manifestPath, "utf8"));
|
|
10542
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
10543
|
+
} catch {
|
|
10544
|
+
return null;
|
|
10545
|
+
}
|
|
10546
|
+
}
|
|
10547
|
+
function readContextFromWorkspace() {
|
|
10548
|
+
const candidate = path27.join(process.cwd(), "factorai.sh.json");
|
|
10549
|
+
const parsed = readJsonFile(candidate);
|
|
10550
|
+
if (!parsed) {
|
|
10551
|
+
return null;
|
|
10552
|
+
}
|
|
10553
|
+
const context = normalizeContext(parsed);
|
|
10554
|
+
return context;
|
|
10555
|
+
}
|
|
10556
|
+
function loadFactorAiAppContext() {
|
|
10557
|
+
return readContextFromWorkspace();
|
|
10558
|
+
}
|
|
10559
|
+
function formatFactorAiAppContextSummary(context) {
|
|
10560
|
+
if (!context) {
|
|
10561
|
+
return "";
|
|
10562
|
+
}
|
|
10563
|
+
const parts = [
|
|
10564
|
+
`appId=${context.appId}`,
|
|
10565
|
+
context.tenantId ? `tenantId=${context.tenantId}` : null,
|
|
10566
|
+
context.projectId ? `projectId=${context.projectId}` : null,
|
|
10567
|
+
context.workspaceId ? `workspaceId=${context.workspaceId}` : null,
|
|
10568
|
+
context.revisionId ? `revisionId=${context.revisionId}` : null,
|
|
10569
|
+
context.deploymentId ? `deploymentId=${context.deploymentId}` : null,
|
|
10570
|
+
context.buildJobId ? `buildJobId=${context.buildJobId}` : null,
|
|
10571
|
+
context.appUrl ? `appUrl=${context.appUrl}` : null
|
|
10572
|
+
].filter(Boolean);
|
|
10573
|
+
return parts.join(" | ");
|
|
10574
|
+
}
|
|
10575
|
+
function buildFactorAiWorkspaceManifest(input) {
|
|
10576
|
+
const projectDir = input.projectDir || process.cwd();
|
|
10577
|
+
const existing = readFactorAiWorkspaceManifest(projectDir) || {};
|
|
10578
|
+
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
10579
|
+
const nextAppContext = {
|
|
10580
|
+
...typeof existing.appContext === "object" && existing.appContext ? existing.appContext : {},
|
|
10581
|
+
appId: input.appContext.appId,
|
|
10582
|
+
tenantId: input.appContext.tenantId ?? null,
|
|
10583
|
+
projectId: input.appContext.projectId ?? null,
|
|
10584
|
+
workspaceId: input.appContext.workspaceId ?? null,
|
|
10585
|
+
revisionId: input.appContext.revisionId ?? null,
|
|
10586
|
+
deploymentId: input.appContext.deploymentId ?? null,
|
|
10587
|
+
buildJobId: input.appContext.buildJobId ?? null,
|
|
10588
|
+
manifestPath: "factorai.sh.json",
|
|
10589
|
+
manifestFile: "factorai.sh.json",
|
|
10590
|
+
appUrl: input.appContext.appUrl ?? null
|
|
10591
|
+
};
|
|
10592
|
+
return {
|
|
10593
|
+
...existing,
|
|
10594
|
+
version: 1,
|
|
10595
|
+
manifestFile: "factorai.sh.json",
|
|
10596
|
+
manifestPath: "factorai.sh.json",
|
|
10597
|
+
appContext: nextAppContext,
|
|
10598
|
+
app: {
|
|
10599
|
+
...typeof existing.app === "object" && existing.app ? existing.app : {},
|
|
10600
|
+
name: input.name ?? (typeof existing.app === "object" && existing.app ? existing.app.name : void 0) ?? null,
|
|
10601
|
+
status: input.status ?? (typeof existing.app === "object" && existing.app ? existing.app.status : void 0) ?? "building",
|
|
10602
|
+
isRedeploy: input.isRedeploy ?? (typeof existing.app === "object" && existing.app ? existing.app.isRedeploy : false),
|
|
10603
|
+
message: input.message ?? (typeof existing.app === "object" && existing.app ? existing.app.message : null),
|
|
10604
|
+
...input.app || {}
|
|
10605
|
+
},
|
|
10606
|
+
agent: {
|
|
10607
|
+
provider: "bluma",
|
|
10608
|
+
mode: "tool-first",
|
|
10609
|
+
instructions: [
|
|
10610
|
+
"Read factorai.sh.json as the source of truth before editing files.",
|
|
10611
|
+
"Prefer incremental changes and preserve the existing deployment context.",
|
|
10612
|
+
"After edits, use the redeploy tool instead of rebuilding from scratch."
|
|
10613
|
+
],
|
|
10614
|
+
...typeof existing.agent === "object" && existing.agent ? existing.agent : {},
|
|
10615
|
+
...input.agent || {}
|
|
10616
|
+
},
|
|
10617
|
+
sandbox: {
|
|
10618
|
+
...typeof existing.sandbox === "object" && existing.sandbox ? existing.sandbox : {},
|
|
10619
|
+
...input.sandbox || {}
|
|
10620
|
+
},
|
|
10621
|
+
source: {
|
|
10622
|
+
root: ".",
|
|
10623
|
+
publicDir: "./public",
|
|
10624
|
+
appDir: ".",
|
|
10625
|
+
...typeof existing.source === "object" && existing.source ? existing.source : {},
|
|
10626
|
+
...input.source || {}
|
|
10627
|
+
},
|
|
10628
|
+
updatedAt: now2,
|
|
10629
|
+
...input.extra || {}
|
|
10630
|
+
};
|
|
10631
|
+
}
|
|
10632
|
+
async function writeFactorAiWorkspaceManifest(input) {
|
|
10633
|
+
const projectDir = input.projectDir || process.cwd();
|
|
10634
|
+
const manifest = buildFactorAiWorkspaceManifest({ ...input, projectDir });
|
|
10635
|
+
const manifestPath = path27.join(projectDir, "factorai.sh.json");
|
|
10636
|
+
fs25.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
10637
|
+
`, "utf8");
|
|
10638
|
+
return { manifestPath, manifest };
|
|
10639
|
+
}
|
|
10640
|
+
|
|
10405
10641
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
10406
10642
|
init_sandbox_policy();
|
|
10643
|
+
function getFactorAiBaseUrl() {
|
|
10644
|
+
const value = process.env.FACTORAI_BASE_URL || process.env.FACTORAI_URL || "";
|
|
10645
|
+
const trimmed = value.trim();
|
|
10646
|
+
return trimmed || void 0;
|
|
10647
|
+
}
|
|
10648
|
+
function getFactorAiApiKey() {
|
|
10649
|
+
const value = process.env.FACTORAI_API_KEY || process.env.FACTORAI_TOKEN || "";
|
|
10650
|
+
const trimmed = value.trim();
|
|
10651
|
+
return trimmed || void 0;
|
|
10652
|
+
}
|
|
10653
|
+
function isFactorAiSandboxEnabled() {
|
|
10654
|
+
const policy = getSandboxPolicy();
|
|
10655
|
+
return policy.isSandbox && Boolean(getFactorAiBaseUrl());
|
|
10656
|
+
}
|
|
10657
|
+
async function requestFactorAi(pathname, init = {}) {
|
|
10658
|
+
const baseUrl = getFactorAiBaseUrl();
|
|
10659
|
+
if (!baseUrl) {
|
|
10660
|
+
throw new Error("FACTORAI_BASE_URL is not configured.");
|
|
10661
|
+
}
|
|
10662
|
+
const headers = new Headers(init.headers || {});
|
|
10663
|
+
headers.set("Content-Type", "application/json");
|
|
10664
|
+
const apiKey = getFactorAiApiKey();
|
|
10665
|
+
if (apiKey) {
|
|
10666
|
+
headers.set("Authorization", `Bearer ${apiKey}`);
|
|
10667
|
+
}
|
|
10668
|
+
const response = await fetch(new URL(pathname, baseUrl), {
|
|
10669
|
+
...init,
|
|
10670
|
+
headers
|
|
10671
|
+
});
|
|
10672
|
+
const text = await response.text();
|
|
10673
|
+
let payload = null;
|
|
10674
|
+
if (text) {
|
|
10675
|
+
try {
|
|
10676
|
+
payload = JSON.parse(text);
|
|
10677
|
+
} catch {
|
|
10678
|
+
payload = text;
|
|
10679
|
+
}
|
|
10680
|
+
}
|
|
10681
|
+
if (!response.ok) {
|
|
10682
|
+
const message2 = payload?.error?.message || payload?.message || payload?.error || `FactorAI request failed with status ${response.status}`;
|
|
10683
|
+
throw new Error(message2);
|
|
10684
|
+
}
|
|
10685
|
+
return payload;
|
|
10686
|
+
}
|
|
10687
|
+
async function factorAiGetAppStatus(args) {
|
|
10688
|
+
const appId = String(args?.appId || "").trim();
|
|
10689
|
+
if (!appId) {
|
|
10690
|
+
return { error: "appId is required." };
|
|
10691
|
+
}
|
|
10692
|
+
return requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
|
|
10693
|
+
}
|
|
10694
|
+
async function factorAiApplyAppChanges(args) {
|
|
10695
|
+
const appId = String(args?.appId || "").trim();
|
|
10696
|
+
if (!appId) {
|
|
10697
|
+
return { error: "appId is required." };
|
|
10698
|
+
}
|
|
10699
|
+
const files = Array.isArray(args?.files) ? args.files : Array.isArray(args?.changes) ? args.changes : [];
|
|
10700
|
+
if (files.length === 0) {
|
|
10701
|
+
return { error: "files or changes must be a non-empty array." };
|
|
10702
|
+
}
|
|
10703
|
+
const response = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}/changes`, {
|
|
10704
|
+
method: "POST",
|
|
10705
|
+
body: JSON.stringify({
|
|
10706
|
+
files,
|
|
10707
|
+
deploy: args?.deploy !== false
|
|
10708
|
+
})
|
|
10709
|
+
});
|
|
10710
|
+
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
10711
|
+
return response;
|
|
10712
|
+
}
|
|
10713
|
+
async function factorAiRedeployApp(args) {
|
|
10714
|
+
const appId = String(args?.appId || "").trim();
|
|
10715
|
+
if (!appId) {
|
|
10716
|
+
return { error: "appId is required." };
|
|
10717
|
+
}
|
|
10718
|
+
const response = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}/redeploy`, {
|
|
10719
|
+
method: "POST"
|
|
10720
|
+
});
|
|
10721
|
+
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
10722
|
+
return response;
|
|
10723
|
+
}
|
|
10724
|
+
function extractFactorAiResponseData(response) {
|
|
10725
|
+
if (response && typeof response === "object" && response.data && typeof response.data === "object") {
|
|
10726
|
+
return response.data;
|
|
10727
|
+
}
|
|
10728
|
+
return response;
|
|
10729
|
+
}
|
|
10730
|
+
async function persistFactorAiWorkspaceManifestFromResponse(response) {
|
|
10731
|
+
const data = extractFactorAiResponseData(response);
|
|
10732
|
+
const appContext = data?.appContext;
|
|
10733
|
+
if (!appContext || typeof appContext.appId !== "string" || !appContext.appId.trim()) {
|
|
10734
|
+
return;
|
|
10735
|
+
}
|
|
10736
|
+
await writeFactorAiWorkspaceManifest({
|
|
10737
|
+
projectDir: process.cwd(),
|
|
10738
|
+
appContext: {
|
|
10739
|
+
appId: String(appContext.appId).trim(),
|
|
10740
|
+
tenantId: appContext.tenantId ?? null,
|
|
10741
|
+
projectId: appContext.projectId ?? null,
|
|
10742
|
+
workspaceId: appContext.workspaceId ?? null,
|
|
10743
|
+
revisionId: appContext.revisionId ?? null,
|
|
10744
|
+
deploymentId: appContext.deploymentId ?? null,
|
|
10745
|
+
buildJobId: appContext.buildJobId ?? null,
|
|
10746
|
+
manifestPath: appContext.manifestPath ?? appContext.manifestFile ?? "factorai.sh.json",
|
|
10747
|
+
manifestFile: appContext.manifestFile ?? appContext.manifestPath ?? "factorai.sh.json",
|
|
10748
|
+
appUrl: appContext.appUrl ?? data?.url ?? null
|
|
10749
|
+
},
|
|
10750
|
+
name: data?.name ?? data?.app?.name ?? void 0,
|
|
10751
|
+
status: data?.status ?? "building",
|
|
10752
|
+
isRedeploy: Boolean(data?.isRedeploy),
|
|
10753
|
+
message: typeof data?.message === "string" ? data.message : null,
|
|
10754
|
+
app: {
|
|
10755
|
+
...typeof data?.app === "object" && data.app ? data.app : {},
|
|
10756
|
+
url: data?.url ?? data?.app?.url ?? null
|
|
10757
|
+
},
|
|
10758
|
+
agent: typeof data?.agent === "object" && data.agent ? data.agent : void 0,
|
|
10759
|
+
sandbox: typeof data?.sandbox === "object" && data.sandbox ? data.sandbox : void 0,
|
|
10760
|
+
source: typeof data?.source === "object" && data.source ? data.source : void 0,
|
|
10761
|
+
extra: {
|
|
10762
|
+
lastUpdatedFromTool: "factorai.sh.apply_app_changes"
|
|
10763
|
+
}
|
|
10764
|
+
});
|
|
10765
|
+
}
|
|
10766
|
+
function getFactorAiSandboxToolDefinitions() {
|
|
10767
|
+
if (!isFactorAiSandboxEnabled()) {
|
|
10768
|
+
return [];
|
|
10769
|
+
}
|
|
10770
|
+
return [
|
|
10771
|
+
{
|
|
10772
|
+
type: "function",
|
|
10773
|
+
function: {
|
|
10774
|
+
name: "factorai.sh.create_next_app",
|
|
10775
|
+
description: "Create a new Next.js project in the sandbox workspace for FactorAI deployments.",
|
|
10776
|
+
parameters: {
|
|
10777
|
+
type: "object",
|
|
10778
|
+
properties: {
|
|
10779
|
+
name: { type: "string", description: "Project name." },
|
|
10780
|
+
template: { type: "string", enum: ["minimal", "full"], description: "Project template." },
|
|
10781
|
+
directory: { type: "string", description: "Target directory for the project." }
|
|
10782
|
+
},
|
|
10783
|
+
required: ["name"],
|
|
10784
|
+
additionalProperties: false
|
|
10785
|
+
}
|
|
10786
|
+
}
|
|
10787
|
+
},
|
|
10788
|
+
{
|
|
10789
|
+
type: "function",
|
|
10790
|
+
function: {
|
|
10791
|
+
name: "factorai.sh.deploy_app",
|
|
10792
|
+
description: "Deploy a Next.js project from the sandbox to the FactorAI hosting backend. The returned metadata must be written into factorai.sh.json in the project root for future incremental edits.",
|
|
10793
|
+
parameters: {
|
|
10794
|
+
type: "object",
|
|
10795
|
+
properties: {
|
|
10796
|
+
projectDir: { type: "string", description: "Path to the Next.js project." },
|
|
10797
|
+
name: { type: "string", description: "App name." },
|
|
10798
|
+
severinoUrl: { type: "string", description: "Optional explicit backend URL." },
|
|
10799
|
+
apiKey: { type: "string", description: "Optional explicit API key." }
|
|
10800
|
+
},
|
|
10801
|
+
required: ["projectDir"],
|
|
10802
|
+
additionalProperties: false
|
|
10803
|
+
}
|
|
10804
|
+
}
|
|
10805
|
+
},
|
|
10806
|
+
{
|
|
10807
|
+
type: "function",
|
|
10808
|
+
function: {
|
|
10809
|
+
name: "factorai.sh.get_app_status",
|
|
10810
|
+
description: "Get the current status and contract for a FactorAI deployment in the sandbox.",
|
|
10811
|
+
parameters: {
|
|
10812
|
+
type: "object",
|
|
10813
|
+
properties: {
|
|
10814
|
+
appId: { type: "string", description: "FactorAI app identifier." }
|
|
10815
|
+
},
|
|
10816
|
+
required: ["appId"],
|
|
10817
|
+
additionalProperties: false
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
},
|
|
10821
|
+
{
|
|
10822
|
+
type: "function",
|
|
10823
|
+
function: {
|
|
10824
|
+
name: "factorai.sh.apply_app_changes",
|
|
10825
|
+
description: "Apply incremental file changes to a FactorAI workspace and optionally redeploy.",
|
|
10826
|
+
parameters: {
|
|
10827
|
+
type: "object",
|
|
10828
|
+
properties: {
|
|
10829
|
+
appId: { type: "string", description: "FactorAI app identifier." },
|
|
10830
|
+
files: {
|
|
10831
|
+
type: "array",
|
|
10832
|
+
description: "List of changed files to apply.",
|
|
10833
|
+
items: {
|
|
10834
|
+
type: "object",
|
|
10835
|
+
properties: {
|
|
10836
|
+
path: { type: "string" },
|
|
10837
|
+
content: { type: "string" }
|
|
10838
|
+
},
|
|
10839
|
+
required: ["path", "content"],
|
|
10840
|
+
additionalProperties: false
|
|
10841
|
+
}
|
|
10842
|
+
},
|
|
10843
|
+
changes: {
|
|
10844
|
+
type: "array",
|
|
10845
|
+
description: "Alias for files.",
|
|
10846
|
+
items: {
|
|
10847
|
+
type: "object",
|
|
10848
|
+
properties: {
|
|
10849
|
+
path: { type: "string" },
|
|
10850
|
+
content: { type: "string" }
|
|
10851
|
+
},
|
|
10852
|
+
required: ["path", "content"],
|
|
10853
|
+
additionalProperties: false
|
|
10854
|
+
}
|
|
10855
|
+
},
|
|
10856
|
+
deploy: {
|
|
10857
|
+
type: "boolean",
|
|
10858
|
+
description: "Redeploy after applying changes. Defaults to true."
|
|
10859
|
+
}
|
|
10860
|
+
},
|
|
10861
|
+
required: ["appId"],
|
|
10862
|
+
additionalProperties: false
|
|
10863
|
+
}
|
|
10864
|
+
}
|
|
10865
|
+
},
|
|
10866
|
+
{
|
|
10867
|
+
type: "function",
|
|
10868
|
+
function: {
|
|
10869
|
+
name: "factorai.sh.redeploy_app",
|
|
10870
|
+
description: "Redeploy the current revision of a FactorAI app.",
|
|
10871
|
+
parameters: {
|
|
10872
|
+
type: "object",
|
|
10873
|
+
properties: {
|
|
10874
|
+
appId: { type: "string", description: "FactorAI app identifier." }
|
|
10875
|
+
},
|
|
10876
|
+
required: ["appId"],
|
|
10877
|
+
additionalProperties: false
|
|
10878
|
+
}
|
|
10879
|
+
}
|
|
10880
|
+
}
|
|
10881
|
+
];
|
|
10882
|
+
}
|
|
10407
10883
|
var NATIVE_TOOL_ENTRIES = [
|
|
10408
10884
|
{
|
|
10409
10885
|
metadata: {
|
|
@@ -10970,25 +11446,63 @@ var NATIVE_TOOL_ENTRIES = [
|
|
|
10970
11446
|
},
|
|
10971
11447
|
{
|
|
10972
11448
|
metadata: {
|
|
10973
|
-
name: "create_next_app",
|
|
11449
|
+
name: "factorai.sh.create_next_app",
|
|
10974
11450
|
category: "filesystem",
|
|
10975
11451
|
riskLevel: "write",
|
|
10976
11452
|
autoApproveInLocal: false,
|
|
10977
11453
|
autoApproveInSandbox: true,
|
|
11454
|
+
sandboxOnly: true,
|
|
10978
11455
|
description: "Create a new Next.js project instantly with App Router, shadcn/ui components, Tailwind CSS, and TypeScript. Templates: minimal (basic structure) or full (with pre-built UI components)."
|
|
10979
11456
|
},
|
|
10980
11457
|
implementation: createNextApp
|
|
10981
11458
|
},
|
|
10982
11459
|
{
|
|
10983
11460
|
metadata: {
|
|
10984
|
-
name: "deploy_app",
|
|
11461
|
+
name: "factorai.sh.deploy_app",
|
|
10985
11462
|
category: "execution",
|
|
10986
11463
|
riskLevel: "network",
|
|
10987
11464
|
autoApproveInLocal: false,
|
|
10988
11465
|
autoApproveInSandbox: true,
|
|
11466
|
+
sandboxOnly: true,
|
|
10989
11467
|
description: "Deploy a Next.js project to Severino. Zips the project (excluding node_modules, .next) and uploads to /api/v1/deploy. Returns appId and live URL."
|
|
10990
11468
|
},
|
|
10991
11469
|
implementation: deployApp
|
|
11470
|
+
},
|
|
11471
|
+
{
|
|
11472
|
+
metadata: {
|
|
11473
|
+
name: "factorai.sh.get_app_status",
|
|
11474
|
+
category: "knowledge",
|
|
11475
|
+
riskLevel: "network",
|
|
11476
|
+
autoApproveInLocal: false,
|
|
11477
|
+
autoApproveInSandbox: true,
|
|
11478
|
+
sandboxOnly: true,
|
|
11479
|
+
description: "Sandbox-only FactorAI tool. Fetch app status and sandbox contract from the deployed backend."
|
|
11480
|
+
},
|
|
11481
|
+
implementation: factorAiGetAppStatus
|
|
11482
|
+
},
|
|
11483
|
+
{
|
|
11484
|
+
metadata: {
|
|
11485
|
+
name: "factorai.sh.apply_app_changes",
|
|
11486
|
+
category: "filesystem",
|
|
11487
|
+
riskLevel: "write",
|
|
11488
|
+
autoApproveInLocal: false,
|
|
11489
|
+
autoApproveInSandbox: true,
|
|
11490
|
+
sandboxOnly: true,
|
|
11491
|
+
description: "Sandbox-only FactorAI tool. Apply file changes to a deployed app workspace and redeploy it."
|
|
11492
|
+
},
|
|
11493
|
+
implementation: factorAiApplyAppChanges
|
|
11494
|
+
},
|
|
11495
|
+
{
|
|
11496
|
+
metadata: {
|
|
11497
|
+
name: "factorai.sh.redeploy_app",
|
|
11498
|
+
category: "execution",
|
|
11499
|
+
riskLevel: "network",
|
|
11500
|
+
autoApproveInLocal: false,
|
|
11501
|
+
autoApproveInSandbox: true,
|
|
11502
|
+
sandboxOnly: true,
|
|
11503
|
+
description: "Sandbox-only FactorAI tool. Trigger redeploy for the current app revision."
|
|
11504
|
+
},
|
|
11505
|
+
implementation: factorAiRedeployApp
|
|
10992
11506
|
}
|
|
10993
11507
|
];
|
|
10994
11508
|
var TOOL_METADATA_MAP = new Map(
|
|
@@ -11004,11 +11518,10 @@ function getNativeToolImplementation(toolName) {
|
|
|
11004
11518
|
return TOOL_IMPLEMENTATION_MAP.get(toolName);
|
|
11005
11519
|
}
|
|
11006
11520
|
function getAllNativeToolMetadata() {
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
return NATIVE_TOOL_ENTRIES.map((entry) => entry.metadata);
|
|
11521
|
+
return NATIVE_TOOL_ENTRIES.filter((entry) => !entry.metadata.sandboxOnly || isFactorAiSandboxEnabled()).map((entry) => entry.metadata);
|
|
11522
|
+
}
|
|
11523
|
+
function getSandboxOnlyNativeToolDefinitions() {
|
|
11524
|
+
return getFactorAiSandboxToolDefinitions();
|
|
11012
11525
|
}
|
|
11013
11526
|
function applyMetadataToToolDefinitions(toolDefinitions) {
|
|
11014
11527
|
return toolDefinitions.map((definition) => {
|
|
@@ -11032,12 +11545,14 @@ var ToolInvoker = class {
|
|
|
11032
11545
|
*/
|
|
11033
11546
|
async initialize() {
|
|
11034
11547
|
try {
|
|
11035
|
-
const
|
|
11036
|
-
const
|
|
11037
|
-
const configPath =
|
|
11038
|
-
const fileContent = await
|
|
11548
|
+
const currentFilePath = fileURLToPath2(import.meta.url);
|
|
11549
|
+
const currentDirPath = path28.dirname(currentFilePath);
|
|
11550
|
+
const configPath = path28.resolve(currentDirPath, "config", "native_tools.json");
|
|
11551
|
+
const fileContent = await fs26.readFile(configPath, "utf-8");
|
|
11039
11552
|
const config2 = JSON.parse(fileContent);
|
|
11040
11553
|
this.toolDefinitions = applyMetadataToToolDefinitions(config2.nativeTools);
|
|
11554
|
+
const sandboxOnlyTools = applyMetadataToToolDefinitions(getSandboxOnlyNativeToolDefinitions());
|
|
11555
|
+
this.toolDefinitions.push(...sandboxOnlyTools);
|
|
11041
11556
|
} catch (error) {
|
|
11042
11557
|
console.error("[ToolInvoker] Erro cr\xEDtico ao carregar 'native_tools.json'. As ferramentas nativas n\xE3o estar\xE3o dispon\xEDveis.", error);
|
|
11043
11558
|
this.toolDefinitions = [];
|
|
@@ -11077,8 +11592,8 @@ var ToolInvoker = class {
|
|
|
11077
11592
|
};
|
|
11078
11593
|
|
|
11079
11594
|
// src/app/agent/tools/mcp/mcp_client.ts
|
|
11080
|
-
import { promises as
|
|
11081
|
-
import
|
|
11595
|
+
import { promises as fs27 } from "fs";
|
|
11596
|
+
import path29 from "path";
|
|
11082
11597
|
import os16 from "os";
|
|
11083
11598
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
11084
11599
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
@@ -11106,9 +11621,9 @@ var MCPClient = class {
|
|
|
11106
11621
|
});
|
|
11107
11622
|
}
|
|
11108
11623
|
const __filename = fileURLToPath3(import.meta.url);
|
|
11109
|
-
const __dirname =
|
|
11110
|
-
const defaultConfigPath =
|
|
11111
|
-
const userConfigPath =
|
|
11624
|
+
const __dirname = path29.dirname(__filename);
|
|
11625
|
+
const defaultConfigPath = path29.resolve(__dirname, "config", "bluma-mcp.json");
|
|
11626
|
+
const userConfigPath = path29.join(os16.homedir(), ".bluma", "bluma-mcp.json");
|
|
11112
11627
|
const defaultConfig = await this.loadMcpConfig(defaultConfigPath, "Default");
|
|
11113
11628
|
const userConfig = await this.loadMcpConfig(userConfigPath, "User");
|
|
11114
11629
|
const mergedConfig = {
|
|
@@ -11142,7 +11657,7 @@ var MCPClient = class {
|
|
|
11142
11657
|
}
|
|
11143
11658
|
async loadMcpConfig(configPath, configType) {
|
|
11144
11659
|
try {
|
|
11145
|
-
const fileContent = await
|
|
11660
|
+
const fileContent = await fs27.readFile(configPath, "utf-8");
|
|
11146
11661
|
const processedContent = this.replaceEnvPlaceholders(fileContent);
|
|
11147
11662
|
return JSON.parse(processedContent);
|
|
11148
11663
|
} catch (error) {
|
|
@@ -11319,13 +11834,13 @@ PENALTY APPLIED: ${penalty.toFixed(1)} points deducted.
|
|
|
11319
11834
|
};
|
|
11320
11835
|
|
|
11321
11836
|
// src/app/agent/bluma/core/bluma.ts
|
|
11322
|
-
import
|
|
11837
|
+
import path39 from "path";
|
|
11323
11838
|
import { v4 as uuidv48 } from "uuid";
|
|
11324
11839
|
|
|
11325
11840
|
// src/app/agent/session_manager/session_manager.ts
|
|
11326
|
-
import
|
|
11841
|
+
import path30 from "path";
|
|
11327
11842
|
import os17 from "os";
|
|
11328
|
-
import { promises as
|
|
11843
|
+
import { promises as fs28 } from "fs";
|
|
11329
11844
|
var fileLocks = /* @__PURE__ */ new Map();
|
|
11330
11845
|
async function withFileLock(file, fn) {
|
|
11331
11846
|
const prev = fileLocks.get(file) || Promise.resolve();
|
|
@@ -11361,13 +11876,13 @@ function debouncedSave(sessionFile, history, memory) {
|
|
|
11361
11876
|
function expandHome(p) {
|
|
11362
11877
|
if (!p) return p;
|
|
11363
11878
|
if (p.startsWith("~")) {
|
|
11364
|
-
return
|
|
11879
|
+
return path30.join(os17.homedir(), p.slice(1));
|
|
11365
11880
|
}
|
|
11366
11881
|
return p;
|
|
11367
11882
|
}
|
|
11368
11883
|
function getPreferredAppDir() {
|
|
11369
|
-
const fixed =
|
|
11370
|
-
return
|
|
11884
|
+
const fixed = path30.join(os17.homedir(), ".bluma");
|
|
11885
|
+
return path30.resolve(expandHome(fixed));
|
|
11371
11886
|
}
|
|
11372
11887
|
async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
11373
11888
|
let attempt = 0;
|
|
@@ -11375,10 +11890,10 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11375
11890
|
const isWin = process.platform === "win32";
|
|
11376
11891
|
while (attempt <= maxRetries) {
|
|
11377
11892
|
try {
|
|
11378
|
-
const dir =
|
|
11379
|
-
await
|
|
11893
|
+
const dir = path30.dirname(dest);
|
|
11894
|
+
await fs28.mkdir(dir, { recursive: true }).catch(() => {
|
|
11380
11895
|
});
|
|
11381
|
-
await
|
|
11896
|
+
await fs28.rename(src, dest);
|
|
11382
11897
|
return;
|
|
11383
11898
|
} catch (e) {
|
|
11384
11899
|
lastErr = e;
|
|
@@ -11391,13 +11906,13 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11391
11906
|
}
|
|
11392
11907
|
}
|
|
11393
11908
|
try {
|
|
11394
|
-
await
|
|
11395
|
-
const data = await
|
|
11396
|
-
const dir =
|
|
11397
|
-
await
|
|
11909
|
+
await fs28.access(src);
|
|
11910
|
+
const data = await fs28.readFile(src);
|
|
11911
|
+
const dir = path30.dirname(dest);
|
|
11912
|
+
await fs28.mkdir(dir, { recursive: true }).catch(() => {
|
|
11398
11913
|
});
|
|
11399
|
-
await
|
|
11400
|
-
await
|
|
11914
|
+
await fs28.writeFile(dest, data);
|
|
11915
|
+
await fs28.unlink(src).catch(() => {
|
|
11401
11916
|
});
|
|
11402
11917
|
return;
|
|
11403
11918
|
} catch (fallbackErr) {
|
|
@@ -11410,16 +11925,16 @@ async function safeRenameWithRetry(src, dest, maxRetries = 6) {
|
|
|
11410
11925
|
}
|
|
11411
11926
|
async function ensureSessionDir() {
|
|
11412
11927
|
const appDir = getPreferredAppDir();
|
|
11413
|
-
const sessionDir =
|
|
11414
|
-
await
|
|
11928
|
+
const sessionDir = path30.join(appDir, "sessions");
|
|
11929
|
+
await fs28.mkdir(sessionDir, { recursive: true });
|
|
11415
11930
|
return sessionDir;
|
|
11416
11931
|
}
|
|
11417
11932
|
async function loadOrcreateSession(sessionId) {
|
|
11418
11933
|
const sessionDir = await ensureSessionDir();
|
|
11419
|
-
const sessionFile =
|
|
11934
|
+
const sessionFile = path30.join(sessionDir, `${sessionId}.json`);
|
|
11420
11935
|
try {
|
|
11421
|
-
await
|
|
11422
|
-
const fileContent = await
|
|
11936
|
+
await fs28.access(sessionFile);
|
|
11937
|
+
const fileContent = await fs28.readFile(sessionFile, "utf-8");
|
|
11423
11938
|
const sessionData = JSON.parse(fileContent);
|
|
11424
11939
|
const memory = {
|
|
11425
11940
|
historyAnchor: sessionData.history_anchor ?? null,
|
|
@@ -11432,7 +11947,7 @@ async function loadOrcreateSession(sessionId) {
|
|
|
11432
11947
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11433
11948
|
conversation_history: []
|
|
11434
11949
|
};
|
|
11435
|
-
await
|
|
11950
|
+
await fs28.writeFile(sessionFile, JSON.stringify(newSessionData, null, 2), "utf-8");
|
|
11436
11951
|
const emptyMemory = {
|
|
11437
11952
|
historyAnchor: null,
|
|
11438
11953
|
compressedTurnSliceCount: 0
|
|
@@ -11444,12 +11959,12 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11444
11959
|
await withFileLock(sessionFile, async () => {
|
|
11445
11960
|
let sessionData;
|
|
11446
11961
|
try {
|
|
11447
|
-
const dir =
|
|
11448
|
-
await
|
|
11962
|
+
const dir = path30.dirname(sessionFile);
|
|
11963
|
+
await fs28.mkdir(dir, { recursive: true });
|
|
11449
11964
|
} catch {
|
|
11450
11965
|
}
|
|
11451
11966
|
try {
|
|
11452
|
-
const fileContent = await
|
|
11967
|
+
const fileContent = await fs28.readFile(sessionFile, "utf-8");
|
|
11453
11968
|
sessionData = JSON.parse(fileContent);
|
|
11454
11969
|
} catch (error) {
|
|
11455
11970
|
const code = error && error.code;
|
|
@@ -11460,14 +11975,14 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11460
11975
|
console.warn(`An unknown error occurred while reading ${sessionFile}. Re-initializing.`, error);
|
|
11461
11976
|
}
|
|
11462
11977
|
}
|
|
11463
|
-
const sessionId =
|
|
11978
|
+
const sessionId = path30.basename(sessionFile, ".json");
|
|
11464
11979
|
sessionData = {
|
|
11465
11980
|
session_id: sessionId,
|
|
11466
11981
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11467
11982
|
conversation_history: []
|
|
11468
11983
|
};
|
|
11469
11984
|
try {
|
|
11470
|
-
await
|
|
11985
|
+
await fs28.writeFile(sessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
11471
11986
|
} catch {
|
|
11472
11987
|
}
|
|
11473
11988
|
}
|
|
@@ -11483,7 +11998,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11483
11998
|
}
|
|
11484
11999
|
const tempSessionFile = `${sessionFile}.${Date.now()}.tmp`;
|
|
11485
12000
|
try {
|
|
11486
|
-
await
|
|
12001
|
+
await fs28.writeFile(tempSessionFile, JSON.stringify(sessionData, null, 2), "utf-8");
|
|
11487
12002
|
await safeRenameWithRetry(tempSessionFile, sessionFile);
|
|
11488
12003
|
} catch (writeError) {
|
|
11489
12004
|
if (writeError instanceof Error) {
|
|
@@ -11492,7 +12007,7 @@ async function doSaveSessionHistory(sessionFile, history, memory) {
|
|
|
11492
12007
|
console.error(`An unknown fatal error occurred while saving session to ${sessionFile}:`, writeError);
|
|
11493
12008
|
}
|
|
11494
12009
|
try {
|
|
11495
|
-
await
|
|
12010
|
+
await fs28.unlink(tempSessionFile);
|
|
11496
12011
|
} catch {
|
|
11497
12012
|
}
|
|
11498
12013
|
}
|
|
@@ -11510,13 +12025,13 @@ async function saveSessionHistory(sessionFile, history, memory) {
|
|
|
11510
12025
|
|
|
11511
12026
|
// src/app/agent/core/prompt/prompt_builder.ts
|
|
11512
12027
|
import os22 from "os";
|
|
11513
|
-
import
|
|
11514
|
-
import
|
|
12028
|
+
import fs35 from "fs";
|
|
12029
|
+
import path37 from "path";
|
|
11515
12030
|
import { execSync as execSync4 } from "child_process";
|
|
11516
12031
|
|
|
11517
12032
|
// src/app/agent/skills/skill_loader.ts
|
|
11518
|
-
import
|
|
11519
|
-
import
|
|
12033
|
+
import fs29 from "fs";
|
|
12034
|
+
import path31 from "path";
|
|
11520
12035
|
import os18 from "os";
|
|
11521
12036
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
11522
12037
|
var SkillLoader = class _SkillLoader {
|
|
@@ -11526,8 +12041,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
11526
12041
|
cache = /* @__PURE__ */ new Map();
|
|
11527
12042
|
conflicts = [];
|
|
11528
12043
|
constructor(projectRoot, bundledDir) {
|
|
11529
|
-
this.projectSkillsDir =
|
|
11530
|
-
this.globalSkillsDir =
|
|
12044
|
+
this.projectSkillsDir = path31.join(projectRoot, ".bluma", "skills");
|
|
12045
|
+
this.globalSkillsDir = path31.join(os18.homedir(), ".bluma", "skills");
|
|
11531
12046
|
this.bundledSkillsDir = bundledDir || _SkillLoader.resolveBundledDir();
|
|
11532
12047
|
}
|
|
11533
12048
|
/**
|
|
@@ -11536,48 +12051,48 @@ var SkillLoader = class _SkillLoader {
|
|
|
11536
12051
|
*/
|
|
11537
12052
|
static resolveBundledDir() {
|
|
11538
12053
|
if (process.env.JEST_WORKER_ID !== void 0 || process.env.NODE_ENV === "test") {
|
|
11539
|
-
return
|
|
12054
|
+
return path31.join(process.cwd(), "dist", "config", "skills");
|
|
11540
12055
|
}
|
|
11541
12056
|
const candidates = [];
|
|
11542
12057
|
const push = (p) => {
|
|
11543
|
-
const abs =
|
|
12058
|
+
const abs = path31.resolve(p);
|
|
11544
12059
|
if (!candidates.includes(abs)) {
|
|
11545
12060
|
candidates.push(abs);
|
|
11546
12061
|
}
|
|
11547
12062
|
};
|
|
11548
12063
|
let argvBundled = null;
|
|
11549
12064
|
try {
|
|
11550
|
-
const bundleDir =
|
|
11551
|
-
push(
|
|
12065
|
+
const bundleDir = path31.dirname(fileURLToPath4(import.meta.url));
|
|
12066
|
+
push(path31.join(bundleDir, "config", "skills"));
|
|
11552
12067
|
} catch {
|
|
11553
12068
|
}
|
|
11554
12069
|
const argv1 = process.argv[1];
|
|
11555
12070
|
if (argv1 && !argv1.startsWith("-")) {
|
|
11556
12071
|
try {
|
|
11557
12072
|
let resolved = argv1;
|
|
11558
|
-
if (
|
|
11559
|
-
resolved =
|
|
11560
|
-
} else if (!
|
|
11561
|
-
resolved =
|
|
12073
|
+
if (path31.isAbsolute(argv1) && fs29.existsSync(argv1)) {
|
|
12074
|
+
resolved = fs29.realpathSync(argv1);
|
|
12075
|
+
} else if (!path31.isAbsolute(argv1)) {
|
|
12076
|
+
resolved = path31.resolve(process.cwd(), argv1);
|
|
11562
12077
|
}
|
|
11563
|
-
const scriptDir =
|
|
11564
|
-
argvBundled =
|
|
12078
|
+
const scriptDir = path31.dirname(resolved);
|
|
12079
|
+
argvBundled = path31.join(scriptDir, "config", "skills");
|
|
11565
12080
|
push(argvBundled);
|
|
11566
12081
|
} catch {
|
|
11567
12082
|
}
|
|
11568
12083
|
}
|
|
11569
12084
|
for (const abs of candidates) {
|
|
11570
|
-
if (
|
|
12085
|
+
if (fs29.existsSync(abs)) {
|
|
11571
12086
|
return abs;
|
|
11572
12087
|
}
|
|
11573
12088
|
}
|
|
11574
12089
|
try {
|
|
11575
|
-
return
|
|
12090
|
+
return path31.join(path31.dirname(fileURLToPath4(import.meta.url)), "config", "skills");
|
|
11576
12091
|
} catch {
|
|
11577
12092
|
if (argvBundled) {
|
|
11578
12093
|
return argvBundled;
|
|
11579
12094
|
}
|
|
11580
|
-
return
|
|
12095
|
+
return path31.join(os18.homedir(), ".bluma", "__bundled_skills_unresolved__");
|
|
11581
12096
|
}
|
|
11582
12097
|
}
|
|
11583
12098
|
/**
|
|
@@ -11606,8 +12121,8 @@ var SkillLoader = class _SkillLoader {
|
|
|
11606
12121
|
this.conflicts.push({
|
|
11607
12122
|
name: skill.name,
|
|
11608
12123
|
userSource: source,
|
|
11609
|
-
userPath:
|
|
11610
|
-
bundledPath:
|
|
12124
|
+
userPath: path31.join(dir, skill.name, "SKILL.md"),
|
|
12125
|
+
bundledPath: path31.join(this.bundledSkillsDir, skill.name, "SKILL.md")
|
|
11611
12126
|
});
|
|
11612
12127
|
continue;
|
|
11613
12128
|
}
|
|
@@ -11615,20 +12130,20 @@ var SkillLoader = class _SkillLoader {
|
|
|
11615
12130
|
}
|
|
11616
12131
|
}
|
|
11617
12132
|
listFromDir(dir, source) {
|
|
11618
|
-
if (!
|
|
12133
|
+
if (!fs29.existsSync(dir)) return [];
|
|
11619
12134
|
try {
|
|
11620
|
-
return
|
|
11621
|
-
const fullPath =
|
|
11622
|
-
return
|
|
11623
|
-
}).map((d) => this.loadMetadataFromPath(
|
|
12135
|
+
return fs29.readdirSync(dir).filter((d) => {
|
|
12136
|
+
const fullPath = path31.join(dir, d);
|
|
12137
|
+
return fs29.statSync(fullPath).isDirectory() && fs29.existsSync(path31.join(fullPath, "SKILL.md"));
|
|
12138
|
+
}).map((d) => this.loadMetadataFromPath(path31.join(dir, d, "SKILL.md"), d, source)).filter((m) => m !== null);
|
|
11624
12139
|
} catch {
|
|
11625
12140
|
return [];
|
|
11626
12141
|
}
|
|
11627
12142
|
}
|
|
11628
12143
|
loadMetadataFromPath(skillPath, skillName, source) {
|
|
11629
|
-
if (!
|
|
12144
|
+
if (!fs29.existsSync(skillPath)) return null;
|
|
11630
12145
|
try {
|
|
11631
|
-
const raw =
|
|
12146
|
+
const raw = fs29.readFileSync(skillPath, "utf-8");
|
|
11632
12147
|
const parsed = this.parseFrontmatter(raw);
|
|
11633
12148
|
return {
|
|
11634
12149
|
name: parsed.name || skillName,
|
|
@@ -11650,12 +12165,12 @@ var SkillLoader = class _SkillLoader {
|
|
|
11650
12165
|
*/
|
|
11651
12166
|
load(name) {
|
|
11652
12167
|
if (this.cache.has(name)) return this.cache.get(name);
|
|
11653
|
-
const bundledPath =
|
|
11654
|
-
const projectPath =
|
|
11655
|
-
const globalPath =
|
|
11656
|
-
const existsBundled =
|
|
11657
|
-
const existsProject =
|
|
11658
|
-
const existsGlobal =
|
|
12168
|
+
const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
12169
|
+
const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
|
|
12170
|
+
const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
|
|
12171
|
+
const existsBundled = fs29.existsSync(bundledPath);
|
|
12172
|
+
const existsProject = fs29.existsSync(projectPath);
|
|
12173
|
+
const existsGlobal = fs29.existsSync(globalPath);
|
|
11659
12174
|
if (existsBundled && (existsProject || existsGlobal)) {
|
|
11660
12175
|
const conflictSource = existsProject ? "project" : "global";
|
|
11661
12176
|
const conflictPath = existsProject ? projectPath : globalPath;
|
|
@@ -11694,9 +12209,9 @@ var SkillLoader = class _SkillLoader {
|
|
|
11694
12209
|
}
|
|
11695
12210
|
loadFromPath(skillPath, name, source) {
|
|
11696
12211
|
try {
|
|
11697
|
-
const raw =
|
|
12212
|
+
const raw = fs29.readFileSync(skillPath, "utf-8");
|
|
11698
12213
|
const parsed = this.parseFrontmatter(raw);
|
|
11699
|
-
const skillDir =
|
|
12214
|
+
const skillDir = path31.dirname(skillPath);
|
|
11700
12215
|
return {
|
|
11701
12216
|
name: parsed.name || name,
|
|
11702
12217
|
description: parsed.description || "",
|
|
@@ -11705,22 +12220,22 @@ var SkillLoader = class _SkillLoader {
|
|
|
11705
12220
|
version: parsed.version,
|
|
11706
12221
|
author: parsed.author,
|
|
11707
12222
|
license: parsed.license,
|
|
11708
|
-
references: this.scanAssets(
|
|
11709
|
-
scripts: this.scanAssets(
|
|
12223
|
+
references: this.scanAssets(path31.join(skillDir, "references")),
|
|
12224
|
+
scripts: this.scanAssets(path31.join(skillDir, "scripts"))
|
|
11710
12225
|
};
|
|
11711
12226
|
} catch {
|
|
11712
12227
|
return null;
|
|
11713
12228
|
}
|
|
11714
12229
|
}
|
|
11715
12230
|
scanAssets(dir) {
|
|
11716
|
-
if (!
|
|
12231
|
+
if (!fs29.existsSync(dir)) return [];
|
|
11717
12232
|
try {
|
|
11718
|
-
return
|
|
11719
|
-
const fp =
|
|
11720
|
-
return
|
|
12233
|
+
return fs29.readdirSync(dir).filter((f) => {
|
|
12234
|
+
const fp = path31.join(dir, f);
|
|
12235
|
+
return fs29.statSync(fp).isFile();
|
|
11721
12236
|
}).map((f) => ({
|
|
11722
12237
|
name: f,
|
|
11723
|
-
path:
|
|
12238
|
+
path: path31.resolve(dir, f)
|
|
11724
12239
|
}));
|
|
11725
12240
|
} catch {
|
|
11726
12241
|
return [];
|
|
@@ -11777,10 +12292,10 @@ var SkillLoader = class _SkillLoader {
|
|
|
11777
12292
|
this.cache.clear();
|
|
11778
12293
|
}
|
|
11779
12294
|
exists(name) {
|
|
11780
|
-
const bundledPath =
|
|
11781
|
-
const projectPath =
|
|
11782
|
-
const globalPath =
|
|
11783
|
-
return
|
|
12295
|
+
const bundledPath = path31.join(this.bundledSkillsDir, name, "SKILL.md");
|
|
12296
|
+
const projectPath = path31.join(this.projectSkillsDir, name, "SKILL.md");
|
|
12297
|
+
const globalPath = path31.join(this.globalSkillsDir, name, "SKILL.md");
|
|
12298
|
+
return fs29.existsSync(bundledPath) || fs29.existsSync(projectPath) || fs29.existsSync(globalPath);
|
|
11784
12299
|
}
|
|
11785
12300
|
/**
|
|
11786
12301
|
* Retorna conflitos detetados (skills do utilizador com mesmo nome de nativas).
|
|
@@ -11812,13 +12327,13 @@ var SkillLoader = class _SkillLoader {
|
|
|
11812
12327
|
};
|
|
11813
12328
|
|
|
11814
12329
|
// src/app/agent/core/prompt/workspace_snapshot.ts
|
|
11815
|
-
import
|
|
11816
|
-
import
|
|
12330
|
+
import fs31 from "fs";
|
|
12331
|
+
import path33 from "path";
|
|
11817
12332
|
import { execSync as execSync3 } from "child_process";
|
|
11818
12333
|
|
|
11819
12334
|
// src/app/agent/utils/blumamd.ts
|
|
11820
|
-
import
|
|
11821
|
-
import
|
|
12335
|
+
import fs30 from "fs";
|
|
12336
|
+
import path32 from "path";
|
|
11822
12337
|
import os19 from "os";
|
|
11823
12338
|
import { execSync as execSync2 } from "child_process";
|
|
11824
12339
|
var MEMORY_INSTRUCTION_PROMPT = "Instru\xE7\xF5es de mem\xF3ria do BluMa (BLUMA.md) est\xE3o abaixo. Siga estas instru\xE7\xF5es exatamente como escritas. Estas instru\xE7\xF5es OVERRIDE qualquer comportamento padr\xE3o.";
|
|
@@ -11948,12 +12463,12 @@ var TEXT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
11948
12463
|
function expandIncludePath(includePath, baseDir) {
|
|
11949
12464
|
const cleanPath = includePath.startsWith("@") ? includePath.slice(1) : includePath;
|
|
11950
12465
|
if (cleanPath.startsWith("~")) {
|
|
11951
|
-
return
|
|
12466
|
+
return path32.join(os19.homedir(), cleanPath.slice(1));
|
|
11952
12467
|
}
|
|
11953
|
-
if (
|
|
12468
|
+
if (path32.isAbsolute(cleanPath)) {
|
|
11954
12469
|
return cleanPath;
|
|
11955
12470
|
}
|
|
11956
|
-
return
|
|
12471
|
+
return path32.resolve(baseDir, cleanPath);
|
|
11957
12472
|
}
|
|
11958
12473
|
function processIncludes(content, baseDir, processedFiles) {
|
|
11959
12474
|
const lines = content.split("\n");
|
|
@@ -11962,20 +12477,20 @@ function processIncludes(content, baseDir, processedFiles) {
|
|
|
11962
12477
|
const includeMatch = line.match(/^@\s*([^\s]+)/);
|
|
11963
12478
|
if (includeMatch) {
|
|
11964
12479
|
const includePath = expandIncludePath(includeMatch[1], baseDir);
|
|
11965
|
-
const normalizedPath =
|
|
12480
|
+
const normalizedPath = path32.normalize(includePath);
|
|
11966
12481
|
if (processedFiles.has(normalizedPath)) {
|
|
11967
12482
|
result.push(`<!-- Circular include prevented: ${includeMatch[1]} -->`);
|
|
11968
12483
|
continue;
|
|
11969
12484
|
}
|
|
11970
|
-
const ext =
|
|
12485
|
+
const ext = path32.extname(includePath).toLowerCase();
|
|
11971
12486
|
if (!TEXT_FILE_EXTENSIONS.has(ext)) {
|
|
11972
12487
|
result.push(`<!-- Include skipped (unsupported extension): ${includeMatch[1]} -->`);
|
|
11973
12488
|
continue;
|
|
11974
12489
|
}
|
|
11975
12490
|
try {
|
|
11976
|
-
const includedContent =
|
|
12491
|
+
const includedContent = fs30.readFileSync(includePath, "utf-8");
|
|
11977
12492
|
processedFiles.add(normalizedPath);
|
|
11978
|
-
const processedContent = processIncludes(includedContent,
|
|
12493
|
+
const processedContent = processIncludes(includedContent, path32.dirname(includePath), processedFiles);
|
|
11979
12494
|
result.push(`
|
|
11980
12495
|
<!-- BEGIN INCLUDE ${includeMatch[1]} -->
|
|
11981
12496
|
`);
|
|
@@ -12021,9 +12536,9 @@ function parseFrontmatterPaths(paths) {
|
|
|
12021
12536
|
}
|
|
12022
12537
|
function readMemoryFile(filePath, type, includeBasePath) {
|
|
12023
12538
|
try {
|
|
12024
|
-
const rawContent =
|
|
12025
|
-
const baseDir = includeBasePath ||
|
|
12026
|
-
const processedFiles = /* @__PURE__ */ new Set([
|
|
12539
|
+
const rawContent = fs30.readFileSync(filePath, "utf-8");
|
|
12540
|
+
const baseDir = includeBasePath || path32.dirname(filePath);
|
|
12541
|
+
const processedFiles = /* @__PURE__ */ new Set([path32.normalize(filePath)]);
|
|
12027
12542
|
const { frontmatter, content: withoutFrontmatter } = parseFrontmatter(rawContent);
|
|
12028
12543
|
const globs = parseFrontmatterPaths(frontmatter.paths);
|
|
12029
12544
|
const processedContent = processIncludes(withoutFrontmatter, baseDir, processedFiles);
|
|
@@ -12045,15 +12560,15 @@ function readMemoryFile(filePath, type, includeBasePath) {
|
|
|
12045
12560
|
}
|
|
12046
12561
|
function findGitRoot(startDir) {
|
|
12047
12562
|
let current = startDir;
|
|
12048
|
-
while (current !==
|
|
12049
|
-
const gitPath =
|
|
12563
|
+
while (current !== path32.dirname(current)) {
|
|
12564
|
+
const gitPath = path32.join(current, ".git");
|
|
12050
12565
|
try {
|
|
12051
|
-
if (
|
|
12566
|
+
if (fs30.existsSync(gitPath)) {
|
|
12052
12567
|
return current;
|
|
12053
12568
|
}
|
|
12054
12569
|
} catch {
|
|
12055
12570
|
}
|
|
12056
|
-
current =
|
|
12571
|
+
current = path32.dirname(current);
|
|
12057
12572
|
}
|
|
12058
12573
|
return null;
|
|
12059
12574
|
}
|
|
@@ -12078,17 +12593,17 @@ function getGitUserInfo(cwd) {
|
|
|
12078
12593
|
}
|
|
12079
12594
|
function processRulesDirectory(rulesDir, type, processedPaths, conditionalRule = false) {
|
|
12080
12595
|
const result = [];
|
|
12081
|
-
if (!
|
|
12596
|
+
if (!fs30.existsSync(rulesDir)) {
|
|
12082
12597
|
return result;
|
|
12083
12598
|
}
|
|
12084
12599
|
try {
|
|
12085
|
-
const entries =
|
|
12600
|
+
const entries = fs30.readdirSync(rulesDir, { withFileTypes: true });
|
|
12086
12601
|
for (const entry of entries) {
|
|
12087
|
-
const entryPath =
|
|
12602
|
+
const entryPath = path32.join(rulesDir, entry.name);
|
|
12088
12603
|
if (entry.isDirectory()) {
|
|
12089
12604
|
result.push(...processRulesDirectory(entryPath, type, processedPaths, conditionalRule));
|
|
12090
12605
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
12091
|
-
const normalizedPath =
|
|
12606
|
+
const normalizedPath = path32.normalize(entryPath);
|
|
12092
12607
|
if (processedPaths.has(normalizedPath)) {
|
|
12093
12608
|
continue;
|
|
12094
12609
|
}
|
|
@@ -12124,13 +12639,13 @@ function loadManagedMemory() {
|
|
|
12124
12639
|
function loadUserMemory() {
|
|
12125
12640
|
const files = [];
|
|
12126
12641
|
const homeDir = os19.homedir();
|
|
12127
|
-
const userBlumaDir =
|
|
12128
|
-
const userBlumaMd =
|
|
12642
|
+
const userBlumaDir = path32.join(homeDir, ".bluma");
|
|
12643
|
+
const userBlumaMd = path32.join(userBlumaDir, "BLUMA.md");
|
|
12129
12644
|
const userFile = readMemoryFile(userBlumaMd, "User");
|
|
12130
12645
|
if (userFile && userFile.content.trim()) {
|
|
12131
12646
|
files.push(userFile);
|
|
12132
12647
|
}
|
|
12133
|
-
const userRulesDir =
|
|
12648
|
+
const userRulesDir = path32.join(userBlumaDir, "rules");
|
|
12134
12649
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
12135
12650
|
files.push(...processRulesDirectory(userRulesDir, "User", processedPaths, false));
|
|
12136
12651
|
return files;
|
|
@@ -12142,10 +12657,10 @@ function loadProjectMemory(cwd) {
|
|
|
12142
12657
|
let currentDir = cwd;
|
|
12143
12658
|
const MAX_TRAVERSAL_DEPTH = 20;
|
|
12144
12659
|
let depth = 0;
|
|
12145
|
-
const normalizedGitRoot =
|
|
12146
|
-
while (currentDir !==
|
|
12660
|
+
const normalizedGitRoot = path32.resolve(gitRoot);
|
|
12661
|
+
while (currentDir !== path32.dirname(currentDir) && path32.resolve(currentDir).startsWith(normalizedGitRoot) && depth < MAX_TRAVERSAL_DEPTH) {
|
|
12147
12662
|
dirs.push(currentDir);
|
|
12148
|
-
currentDir =
|
|
12663
|
+
currentDir = path32.dirname(currentDir);
|
|
12149
12664
|
depth++;
|
|
12150
12665
|
}
|
|
12151
12666
|
if (!dirs.includes(gitRoot)) {
|
|
@@ -12153,7 +12668,7 @@ function loadProjectMemory(cwd) {
|
|
|
12153
12668
|
}
|
|
12154
12669
|
const processedPaths = /* @__PURE__ */ new Set();
|
|
12155
12670
|
for (const dir of dirs.reverse()) {
|
|
12156
|
-
const projectBlumaMd =
|
|
12671
|
+
const projectBlumaMd = path32.join(dir, "BLUMA.md");
|
|
12157
12672
|
if (!processedPaths.has(projectBlumaMd)) {
|
|
12158
12673
|
processedPaths.add(projectBlumaMd);
|
|
12159
12674
|
const projectFile = readMemoryFile(projectBlumaMd, "Project");
|
|
@@ -12161,7 +12676,7 @@ function loadProjectMemory(cwd) {
|
|
|
12161
12676
|
files.push(projectFile);
|
|
12162
12677
|
}
|
|
12163
12678
|
}
|
|
12164
|
-
const blumaDirBlumaMd =
|
|
12679
|
+
const blumaDirBlumaMd = path32.join(dir, ".bluma", "BLUMA.md");
|
|
12165
12680
|
if (!processedPaths.has(blumaDirBlumaMd)) {
|
|
12166
12681
|
processedPaths.add(blumaDirBlumaMd);
|
|
12167
12682
|
const blumaDirFile = readMemoryFile(blumaDirBlumaMd, "Project");
|
|
@@ -12169,10 +12684,10 @@ function loadProjectMemory(cwd) {
|
|
|
12169
12684
|
files.push(blumaDirFile);
|
|
12170
12685
|
}
|
|
12171
12686
|
}
|
|
12172
|
-
const rulesDir =
|
|
12687
|
+
const rulesDir = path32.join(dir, ".bluma", "rules");
|
|
12173
12688
|
files.push(...processRulesDirectory(rulesDir, "Project", processedPaths, false));
|
|
12174
12689
|
}
|
|
12175
|
-
const localBlumaMd =
|
|
12690
|
+
const localBlumaMd = path32.join(cwd, "BLUMA.local.md");
|
|
12176
12691
|
if (!processedPaths.has(localBlumaMd)) {
|
|
12177
12692
|
processedPaths.add(localBlumaMd);
|
|
12178
12693
|
const localFile = readMemoryFile(localBlumaMd, "Local");
|
|
@@ -12258,10 +12773,10 @@ var LIMITS = {
|
|
|
12258
12773
|
};
|
|
12259
12774
|
function safeReadFile(filePath, maxChars) {
|
|
12260
12775
|
try {
|
|
12261
|
-
if (!
|
|
12262
|
-
const st =
|
|
12776
|
+
if (!fs31.existsSync(filePath)) return null;
|
|
12777
|
+
const st = fs31.statSync(filePath);
|
|
12263
12778
|
if (!st.isFile()) return null;
|
|
12264
|
-
const raw =
|
|
12779
|
+
const raw = fs31.readFileSync(filePath, "utf8");
|
|
12265
12780
|
if (raw.includes("\0")) return null;
|
|
12266
12781
|
if (raw.length <= maxChars) return raw;
|
|
12267
12782
|
return `${raw.slice(0, maxChars)}
|
|
@@ -12273,7 +12788,7 @@ function safeReadFile(filePath, maxChars) {
|
|
|
12273
12788
|
}
|
|
12274
12789
|
function tryReadReadme(cwd) {
|
|
12275
12790
|
for (const name of ["README.md", "README.MD", "readme.md", "Readme.md"]) {
|
|
12276
|
-
const c = safeReadFile(
|
|
12791
|
+
const c = safeReadFile(path33.join(cwd, name), LIMITS.readme);
|
|
12277
12792
|
if (c) return `(${name})
|
|
12278
12793
|
${c}`;
|
|
12279
12794
|
}
|
|
@@ -12281,14 +12796,14 @@ ${c}`;
|
|
|
12281
12796
|
}
|
|
12282
12797
|
function tryReadBluMaMd(cwd) {
|
|
12283
12798
|
const paths = [
|
|
12284
|
-
|
|
12285
|
-
|
|
12286
|
-
|
|
12799
|
+
path33.join(cwd, "BluMa.md"),
|
|
12800
|
+
path33.join(cwd, "BLUMA.md"),
|
|
12801
|
+
path33.join(cwd, ".bluma", "BluMa.md")
|
|
12287
12802
|
];
|
|
12288
12803
|
for (const p of paths) {
|
|
12289
12804
|
const c = safeReadFile(p, LIMITS.blumaMd);
|
|
12290
12805
|
if (c) {
|
|
12291
|
-
const rel =
|
|
12806
|
+
const rel = path33.relative(cwd, p) || p;
|
|
12292
12807
|
return `(${rel})
|
|
12293
12808
|
${c}`;
|
|
12294
12809
|
}
|
|
@@ -12296,10 +12811,10 @@ ${c}`;
|
|
|
12296
12811
|
return null;
|
|
12297
12812
|
}
|
|
12298
12813
|
function summarizePackageJson(cwd) {
|
|
12299
|
-
const p =
|
|
12814
|
+
const p = path33.join(cwd, "package.json");
|
|
12300
12815
|
try {
|
|
12301
|
-
if (!
|
|
12302
|
-
const pkg = JSON.parse(
|
|
12816
|
+
if (!fs31.existsSync(p)) return null;
|
|
12817
|
+
const pkg = JSON.parse(fs31.readFileSync(p, "utf8"));
|
|
12303
12818
|
const scripts = pkg.scripts;
|
|
12304
12819
|
let scriptKeys = "";
|
|
12305
12820
|
if (scripts && typeof scripts === "object" && !Array.isArray(scripts)) {
|
|
@@ -12332,7 +12847,7 @@ function summarizePackageJson(cwd) {
|
|
|
12332
12847
|
}
|
|
12333
12848
|
function topLevelListing(cwd) {
|
|
12334
12849
|
try {
|
|
12335
|
-
const names =
|
|
12850
|
+
const names = fs31.readdirSync(cwd, { withFileTypes: true });
|
|
12336
12851
|
const sorted = [...names].sort((a, b) => a.name.localeCompare(b.name));
|
|
12337
12852
|
const limited = sorted.slice(0, LIMITS.topDirEntries);
|
|
12338
12853
|
const lines = limited.map((d) => `${d.name}${d.isDirectory() ? "/" : ""}`);
|
|
@@ -12390,7 +12905,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12390
12905
|
parts.push(pkg);
|
|
12391
12906
|
parts.push("```\n");
|
|
12392
12907
|
}
|
|
12393
|
-
const py = safeReadFile(
|
|
12908
|
+
const py = safeReadFile(path33.join(cwd, "pyproject.toml"), LIMITS.pyproject);
|
|
12394
12909
|
if (py) {
|
|
12395
12910
|
parts.push("### pyproject.toml (excerpt)\n```toml");
|
|
12396
12911
|
parts.push(py);
|
|
@@ -12408,15 +12923,15 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12408
12923
|
parts.push(bluma);
|
|
12409
12924
|
parts.push("```\n");
|
|
12410
12925
|
}
|
|
12411
|
-
const contrib = safeReadFile(
|
|
12926
|
+
const contrib = safeReadFile(path33.join(cwd, "CONTRIBUTING.md"), LIMITS.contributing);
|
|
12412
12927
|
if (contrib) {
|
|
12413
12928
|
parts.push("### CONTRIBUTING.md (excerpt)\n```markdown");
|
|
12414
12929
|
parts.push(contrib);
|
|
12415
12930
|
parts.push("```\n");
|
|
12416
12931
|
}
|
|
12417
|
-
const chlog = safeReadFile(
|
|
12932
|
+
const chlog = safeReadFile(path33.join(cwd, "CHANGELOG.md"), LIMITS.changelog);
|
|
12418
12933
|
if (!chlog) {
|
|
12419
|
-
const alt = safeReadFile(
|
|
12934
|
+
const alt = safeReadFile(path33.join(cwd, "CHANGES.md"), LIMITS.changelog);
|
|
12420
12935
|
if (alt) {
|
|
12421
12936
|
parts.push("### CHANGES.md (excerpt)\n```markdown");
|
|
12422
12937
|
parts.push(alt);
|
|
@@ -12452,14 +12967,14 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12452
12967
|
} else {
|
|
12453
12968
|
parts.push("### Git\n(not a git work tree, or `git` unavailable)\n");
|
|
12454
12969
|
}
|
|
12455
|
-
const tsconfig = safeReadFile(
|
|
12970
|
+
const tsconfig = safeReadFile(path33.join(cwd, "tsconfig.json"), LIMITS.tsconfig);
|
|
12456
12971
|
if (tsconfig) {
|
|
12457
12972
|
parts.push("### tsconfig.json (excerpt)\n```json");
|
|
12458
12973
|
parts.push(tsconfig);
|
|
12459
12974
|
parts.push("```\n");
|
|
12460
12975
|
}
|
|
12461
12976
|
for (const name of ["Dockerfile", "dockerfile", "Dockerfile.prod", "Dockerfile.dev"]) {
|
|
12462
|
-
const df = safeReadFile(
|
|
12977
|
+
const df = safeReadFile(path33.join(cwd, name), LIMITS.dockerfile);
|
|
12463
12978
|
if (df) {
|
|
12464
12979
|
parts.push(`### ${name} (excerpt)
|
|
12465
12980
|
`);
|
|
@@ -12469,7 +12984,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12469
12984
|
}
|
|
12470
12985
|
}
|
|
12471
12986
|
for (const name of ["docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml"]) {
|
|
12472
|
-
const dc = safeReadFile(
|
|
12987
|
+
const dc = safeReadFile(path33.join(cwd, name), LIMITS.dockerfile);
|
|
12473
12988
|
if (dc) {
|
|
12474
12989
|
parts.push(`### ${name} (excerpt)
|
|
12475
12990
|
`);
|
|
@@ -12484,12 +12999,12 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12484
12999
|
".circleci/config.yml",
|
|
12485
13000
|
"Jenkinsfile"
|
|
12486
13001
|
]) {
|
|
12487
|
-
const ciFile =
|
|
12488
|
-
if (
|
|
12489
|
-
const st =
|
|
13002
|
+
const ciFile = path33.join(cwd, ciPath);
|
|
13003
|
+
if (fs31.existsSync(ciFile)) {
|
|
13004
|
+
const st = fs31.statSync(ciFile);
|
|
12490
13005
|
if (st.isDirectory()) {
|
|
12491
13006
|
try {
|
|
12492
|
-
const wfFiles =
|
|
13007
|
+
const wfFiles = fs31.readdirSync(ciFile).filter((f) => f.endsWith(".yml") || f.endsWith(".yaml"));
|
|
12493
13008
|
if (wfFiles.length > 0) {
|
|
12494
13009
|
parts.push(`### GitHub Actions workflows
|
|
12495
13010
|
\`${wfFiles.join("`, `")}\`
|
|
@@ -12500,7 +13015,7 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12500
13015
|
} else {
|
|
12501
13016
|
const ci = safeReadFile(ciFile, LIMITS.ciConfig);
|
|
12502
13017
|
if (ci) {
|
|
12503
|
-
parts.push(`### CI config (${
|
|
13018
|
+
parts.push(`### CI config (${path33.basename(ciPath)})
|
|
12504
13019
|
`);
|
|
12505
13020
|
parts.push(ci);
|
|
12506
13021
|
parts.push("\n");
|
|
@@ -12509,8 +13024,8 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12509
13024
|
}
|
|
12510
13025
|
}
|
|
12511
13026
|
for (const depFile of ["requirements.txt", "Pipfile", "poetry.lock", "Gemfile", "go.sum", "Cargo.lock"]) {
|
|
12512
|
-
const depPath =
|
|
12513
|
-
if (
|
|
13027
|
+
const depPath = path33.join(cwd, depFile);
|
|
13028
|
+
if (fs31.existsSync(depPath)) {
|
|
12514
13029
|
const depContent = safeReadFile(depPath, 1500);
|
|
12515
13030
|
if (depContent) {
|
|
12516
13031
|
parts.push(`### ${depFile} (top entries)
|
|
@@ -12523,10 +13038,10 @@ function buildWorkspaceSnapshot(cwd) {
|
|
|
12523
13038
|
}
|
|
12524
13039
|
}
|
|
12525
13040
|
for (const envFile of [".env.example", ".env.sample", ".env.template"]) {
|
|
12526
|
-
const envPath =
|
|
12527
|
-
if (
|
|
13041
|
+
const envPath = path33.join(cwd, envFile);
|
|
13042
|
+
if (fs31.existsSync(envPath)) {
|
|
12528
13043
|
try {
|
|
12529
|
-
const raw =
|
|
13044
|
+
const raw = fs31.readFileSync(envPath, "utf-8");
|
|
12530
13045
|
const keys = raw.split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")).map((l) => {
|
|
12531
13046
|
const eqIndex = l.indexOf("=");
|
|
12532
13047
|
return eqIndex >= 0 ? l.slice(0, eqIndex).trim() : l.trim();
|
|
@@ -12549,15 +13064,15 @@ init_runtime_config();
|
|
|
12549
13064
|
|
|
12550
13065
|
// src/app/agent/runtime/plugin_registry.ts
|
|
12551
13066
|
init_sandbox_policy();
|
|
12552
|
-
import
|
|
13067
|
+
import fs32 from "fs";
|
|
12553
13068
|
import os20 from "os";
|
|
12554
|
-
import
|
|
13069
|
+
import path34 from "path";
|
|
12555
13070
|
function getProjectPluginsDir() {
|
|
12556
13071
|
const policy = getSandboxPolicy();
|
|
12557
|
-
return
|
|
13072
|
+
return path34.join(policy.workspaceRoot, ".bluma", "plugins");
|
|
12558
13073
|
}
|
|
12559
13074
|
function getGlobalPluginsDir() {
|
|
12560
|
-
return
|
|
13075
|
+
return path34.join(process.env.HOME || os20.homedir(), ".bluma", "plugins");
|
|
12561
13076
|
}
|
|
12562
13077
|
function getPluginDirs() {
|
|
12563
13078
|
return {
|
|
@@ -12566,11 +13081,11 @@ function getPluginDirs() {
|
|
|
12566
13081
|
};
|
|
12567
13082
|
}
|
|
12568
13083
|
function readManifest(manifestPath, fallbackName) {
|
|
12569
|
-
if (!
|
|
13084
|
+
if (!fs32.existsSync(manifestPath)) {
|
|
12570
13085
|
return null;
|
|
12571
13086
|
}
|
|
12572
13087
|
try {
|
|
12573
|
-
const parsed = JSON.parse(
|
|
13088
|
+
const parsed = JSON.parse(fs32.readFileSync(manifestPath, "utf-8"));
|
|
12574
13089
|
return {
|
|
12575
13090
|
name: typeof parsed.name === "string" && parsed.name.trim() ? parsed.name.trim() : fallbackName,
|
|
12576
13091
|
description: typeof parsed.description === "string" ? parsed.description.trim() : void 0,
|
|
@@ -12583,22 +13098,22 @@ function readManifest(manifestPath, fallbackName) {
|
|
|
12583
13098
|
}
|
|
12584
13099
|
function findManifestPath(pluginDir) {
|
|
12585
13100
|
const candidates = [
|
|
12586
|
-
|
|
12587
|
-
|
|
13101
|
+
path34.join(pluginDir, ".codex-plugin", "plugin.json"),
|
|
13102
|
+
path34.join(pluginDir, "plugin.json")
|
|
12588
13103
|
];
|
|
12589
13104
|
for (const candidate of candidates) {
|
|
12590
|
-
if (
|
|
13105
|
+
if (fs32.existsSync(candidate)) {
|
|
12591
13106
|
return candidate;
|
|
12592
13107
|
}
|
|
12593
13108
|
}
|
|
12594
13109
|
return null;
|
|
12595
13110
|
}
|
|
12596
13111
|
function listFromDir(baseDir, source) {
|
|
12597
|
-
if (!
|
|
13112
|
+
if (!fs32.existsSync(baseDir)) {
|
|
12598
13113
|
return [];
|
|
12599
13114
|
}
|
|
12600
|
-
return
|
|
12601
|
-
const pluginDir =
|
|
13115
|
+
return fs32.readdirSync(baseDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).flatMap((entry) => {
|
|
13116
|
+
const pluginDir = path34.join(baseDir, entry.name);
|
|
12602
13117
|
const manifestPath = findManifestPath(pluginDir);
|
|
12603
13118
|
if (!manifestPath) {
|
|
12604
13119
|
return [];
|
|
@@ -13041,13 +13556,23 @@ Since you are in an **isolated sandbox**, ALL tools are auto-approved:
|
|
|
13041
13556
|
- \\\`notebook_edit\\\` - Jupyter notebook editing (auto-approved)
|
|
13042
13557
|
- \\\`cron_create\\\` - Schedule reminders (auto-approved)
|
|
13043
13558
|
|
|
13559
|
+
### FactorAI Workspace Tools
|
|
13560
|
+
When a deployed app is attached to this sandbox, the following tools may appear and are sandbox-only:
|
|
13561
|
+
- \\\`factorai.sh.create_next_app\\\` - Create a new Next.js project in the sandbox
|
|
13562
|
+
- \\\`factorai.sh.deploy_app\\\` - Deploy a Next.js project from the sandbox to the hosting backend
|
|
13563
|
+
- \\\`factorai.sh.get_app_status\\\` - Read app status and contract context
|
|
13564
|
+
- \\\`factorai.sh.apply_app_changes\\\` - Apply incremental file changes to the app workspace
|
|
13565
|
+
- \\\`factorai.sh.redeploy_app\\\` - Redeploy the current app revision after edits
|
|
13566
|
+
|
|
13567
|
+
Use \\\`factorai.sh.json\\\` as a normal workspace file. Do not treat manifest reading as a tool call.
|
|
13568
|
+
|
|
13044
13569
|
---
|
|
13045
13570
|
|
|
13046
13571
|
## \u{1F680} NEXT.JS DEPLOY WORKFLOW - SEVERINO INTEGRATION
|
|
13047
13572
|
|
|
13048
13573
|
You have access to **two specialized tools** for creating and deploying Next.js apps to Severino:
|
|
13049
13574
|
|
|
13050
|
-
### Tool 1: \\\`create_next_app\\\`
|
|
13575
|
+
### Tool 1: \\\`factorai.sh.create_next_app\\\`
|
|
13051
13576
|
|
|
13052
13577
|
**Purpose:** Instant scaffold of a complete Next.js project with App Router, shadcn/ui, and Tailwind CSS.
|
|
13053
13578
|
|
|
@@ -13058,7 +13583,7 @@ You have access to **two specialized tools** for creating and deploying Next.js
|
|
|
13058
13583
|
|
|
13059
13584
|
**Example:**
|
|
13060
13585
|
\\\`\\\`\\\`typescript
|
|
13061
|
-
const result = await
|
|
13586
|
+
const result = await factorai.sh.create_next_app({
|
|
13062
13587
|
name: 'erp-dashboard',
|
|
13063
13588
|
template: 'full',
|
|
13064
13589
|
});
|
|
@@ -13079,7 +13604,7 @@ const result = await createNextApp({
|
|
|
13079
13604
|
|
|
13080
13605
|
---
|
|
13081
13606
|
|
|
13082
|
-
### Tool 2: \\\`deploy_app\\\`
|
|
13607
|
+
### Tool 2: \\\`factorai.sh.deploy_app\\\`
|
|
13083
13608
|
|
|
13084
13609
|
**Purpose:** Zip and deploy a Next.js project to Severino for hosting.
|
|
13085
13610
|
|
|
@@ -13091,7 +13616,7 @@ const result = await createNextApp({
|
|
|
13091
13616
|
|
|
13092
13617
|
**Example:**
|
|
13093
13618
|
\\\`\\\`\\\`typescript
|
|
13094
|
-
const result = await
|
|
13619
|
+
const result = await factorai.sh.deploy_app({
|
|
13095
13620
|
projectDir: './erp-dashboard',
|
|
13096
13621
|
name: 'erp-dashboard',
|
|
13097
13622
|
});
|
|
@@ -13112,10 +13637,17 @@ const result = await deployApp({
|
|
|
13112
13637
|
- O ZIP deve conter **apenas c\xF3digo fonte e configs**
|
|
13113
13638
|
- \\\`node_modules\\\` \xE9 recriado no servidor com \\\`npm install\\\`
|
|
13114
13639
|
- Incluir \\\`node_modules\\\` = ZIP de 200MB+ = deploy falha
|
|
13115
|
-
- A tool \\\`deploy_app\\\` j\xE1 exclui automaticamente, mas se fizeres ZIP manual: **exclui \\\`node_modules\\\`**
|
|
13640
|
+
- A tool \\\`factorai.sh.deploy_app\\\` j\xE1 exclui automaticamente, mas se fizeres ZIP manual: **exclui \\\`node_modules\\\`**
|
|
13116
13641
|
|
|
13117
13642
|
**Important:** The deploy is **asynchronous** - status starts as \\\`'building'\\\`. The app will be ready in ~30-60 seconds.
|
|
13118
13643
|
|
|
13644
|
+
**After this tool returns**
|
|
13645
|
+
- Read the returned metadata immediately.
|
|
13646
|
+
- Create or update \\\`factorai.sh.json\\\` in the project root.
|
|
13647
|
+
- Persist the deployment context there: \\\`appId\\\`, \\\`tenantId\\\`, \\\`projectId\\\`, \\\`workspaceId\\\`, \\\`revisionId\\\`, \\\`deploymentId\\\`, \\\`buildJobId\\\`, and \\\`appUrl\\\` when available.
|
|
13648
|
+
- Treat \\\`factorai.sh.json\\\` as the source of truth for future incremental edits and redeploys.
|
|
13649
|
+
- Do not invent a second manifest file or duplicate state elsewhere.
|
|
13650
|
+
|
|
13119
13651
|
---
|
|
13120
13652
|
|
|
13121
13653
|
### Complete Workflow Example
|
|
@@ -13123,7 +13655,7 @@ const result = await deployApp({
|
|
|
13123
13655
|
\\\`\\\`\\\`typescript
|
|
13124
13656
|
// Step 1: Create project
|
|
13125
13657
|
message({ message_type: 'info', content: 'Step 1/3: Creating Next.js project...' });
|
|
13126
|
-
const scaffold = await
|
|
13658
|
+
const scaffold = await factorai.sh.create_next_app({
|
|
13127
13659
|
name: 'erp-dashboard',
|
|
13128
13660
|
template: 'full',
|
|
13129
13661
|
});
|
|
@@ -13137,7 +13669,7 @@ await fileWrite({
|
|
|
13137
13669
|
|
|
13138
13670
|
// Step 3: Deploy
|
|
13139
13671
|
message({ message_type: 'info', content: 'Step 3/3: Deploying to Severino...' });
|
|
13140
|
-
const deploy = await
|
|
13672
|
+
const deploy = await factorai.sh.deploy_app({
|
|
13141
13673
|
projectDir: './erp-dashboard',
|
|
13142
13674
|
name: 'erp-dashboard',
|
|
13143
13675
|
});
|
|
@@ -13155,7 +13687,7 @@ message({
|
|
|
13155
13687
|
### \u26A0\uFE0F Critical Requirements for Deploy
|
|
13156
13688
|
|
|
13157
13689
|
1. **\\\`output: 'standalone'\\\` in \\\`next.config.js\\\`**
|
|
13158
|
-
- The \\\`create_next_app\\\` tool already includes this
|
|
13690
|
+
- The \\\`factorai.sh.create_next_app\\\` tool already includes this
|
|
13159
13691
|
- If you modify \\\`next.config.js\\\`, keep this setting
|
|
13160
13692
|
|
|
13161
13693
|
2. **Build before deploy (optional but recommended)**
|
|
@@ -13493,8 +14025,8 @@ function buildModelInfoSection(modelId) {
|
|
|
13493
14025
|
|
|
13494
14026
|
// src/app/agent/runtime/hook_registry.ts
|
|
13495
14027
|
init_sandbox_policy();
|
|
13496
|
-
import
|
|
13497
|
-
import
|
|
14028
|
+
import fs33 from "fs";
|
|
14029
|
+
import path35 from "path";
|
|
13498
14030
|
var DEFAULT_STATE = {
|
|
13499
14031
|
enabled: true,
|
|
13500
14032
|
maxEvents: 120,
|
|
@@ -13505,7 +14037,7 @@ var cache2 = null;
|
|
|
13505
14037
|
var cachePath2 = null;
|
|
13506
14038
|
function getStatePath() {
|
|
13507
14039
|
const policy = getSandboxPolicy();
|
|
13508
|
-
return
|
|
14040
|
+
return path35.join(policy.workspaceRoot, ".bluma", "hooks.json");
|
|
13509
14041
|
}
|
|
13510
14042
|
function getHookStatePath() {
|
|
13511
14043
|
return getStatePath();
|
|
@@ -13524,8 +14056,8 @@ function ensureLoaded2() {
|
|
|
13524
14056
|
return cache2;
|
|
13525
14057
|
}
|
|
13526
14058
|
try {
|
|
13527
|
-
if (
|
|
13528
|
-
const parsed = JSON.parse(
|
|
14059
|
+
if (fs33.existsSync(statePath)) {
|
|
14060
|
+
const parsed = JSON.parse(fs33.readFileSync(statePath, "utf-8"));
|
|
13529
14061
|
cache2 = {
|
|
13530
14062
|
enabled: typeof parsed.enabled === "boolean" ? parsed.enabled : DEFAULT_STATE.enabled,
|
|
13531
14063
|
maxEvents: typeof parsed.maxEvents === "number" && Number.isFinite(parsed.maxEvents) && parsed.maxEvents > 0 ? Math.floor(parsed.maxEvents) : DEFAULT_STATE.maxEvents,
|
|
@@ -13551,9 +14083,9 @@ function ensureLoaded2() {
|
|
|
13551
14083
|
}
|
|
13552
14084
|
function persist2(state) {
|
|
13553
14085
|
const statePath = getStatePath();
|
|
13554
|
-
|
|
14086
|
+
fs33.mkdirSync(path35.dirname(statePath), { recursive: true });
|
|
13555
14087
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
13556
|
-
|
|
14088
|
+
fs33.writeFileSync(statePath, JSON.stringify(state, null, 2), "utf-8");
|
|
13557
14089
|
cache2 = state;
|
|
13558
14090
|
cachePath2 = statePath;
|
|
13559
14091
|
}
|
|
@@ -13622,18 +14154,18 @@ function buildSystemRemindersSection() {
|
|
|
13622
14154
|
}
|
|
13623
14155
|
|
|
13624
14156
|
// src/app/agent/core/prompt/auto_memory.ts
|
|
13625
|
-
import
|
|
13626
|
-
import
|
|
14157
|
+
import fs34 from "fs";
|
|
14158
|
+
import path36 from "path";
|
|
13627
14159
|
import os21 from "os";
|
|
13628
|
-
var AUTO_MEMORY_FILE =
|
|
14160
|
+
var AUTO_MEMORY_FILE = path36.join(os21.homedir(), ".bluma", "auto_memory.md");
|
|
13629
14161
|
var MAX_AUTO_MEMORY_CHARS = 25e3;
|
|
13630
14162
|
var MAX_AUTO_MEMORY_LINES = 200;
|
|
13631
14163
|
function getAutoMemoryForPrompt() {
|
|
13632
14164
|
try {
|
|
13633
|
-
if (!
|
|
14165
|
+
if (!fs34.existsSync(AUTO_MEMORY_FILE)) {
|
|
13634
14166
|
return "";
|
|
13635
14167
|
}
|
|
13636
|
-
let content =
|
|
14168
|
+
let content = fs34.readFileSync(AUTO_MEMORY_FILE, "utf-8");
|
|
13637
14169
|
if (!content.trim()) {
|
|
13638
14170
|
return "";
|
|
13639
14171
|
}
|
|
@@ -13692,10 +14224,10 @@ function getGitBranch(dir) {
|
|
|
13692
14224
|
}
|
|
13693
14225
|
function getPackageManager(dir) {
|
|
13694
14226
|
try {
|
|
13695
|
-
if (
|
|
13696
|
-
if (
|
|
13697
|
-
if (
|
|
13698
|
-
if (
|
|
14227
|
+
if (fs35.existsSync(path37.join(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
14228
|
+
if (fs35.existsSync(path37.join(dir, "yarn.lock"))) return "yarn";
|
|
14229
|
+
if (fs35.existsSync(path37.join(dir, "bun.lockb"))) return "bun";
|
|
14230
|
+
if (fs35.existsSync(path37.join(dir, "package-lock.json"))) return "npm";
|
|
13699
14231
|
return "unknown";
|
|
13700
14232
|
} catch {
|
|
13701
14233
|
return "unknown";
|
|
@@ -13703,9 +14235,9 @@ function getPackageManager(dir) {
|
|
|
13703
14235
|
}
|
|
13704
14236
|
function getProjectType(dir) {
|
|
13705
14237
|
try {
|
|
13706
|
-
const files =
|
|
14238
|
+
const files = fs35.readdirSync(dir);
|
|
13707
14239
|
if (files.includes("package.json")) {
|
|
13708
|
-
const pkg = JSON.parse(
|
|
14240
|
+
const pkg = JSON.parse(fs35.readFileSync(path37.join(dir, "package.json"), "utf-8"));
|
|
13709
14241
|
if (pkg.dependencies?.next || pkg.devDependencies?.next) return "Next.js";
|
|
13710
14242
|
if (pkg.dependencies?.react || pkg.devDependencies?.react) return "React";
|
|
13711
14243
|
if (pkg.dependencies?.express || pkg.devDependencies?.express) return "Express";
|
|
@@ -13724,9 +14256,9 @@ function getProjectType(dir) {
|
|
|
13724
14256
|
}
|
|
13725
14257
|
function getTestFramework(dir) {
|
|
13726
14258
|
try {
|
|
13727
|
-
const pkgPath =
|
|
13728
|
-
if (
|
|
13729
|
-
const pkg = JSON.parse(
|
|
14259
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
14260
|
+
if (fs35.existsSync(pkgPath)) {
|
|
14261
|
+
const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
|
|
13730
14262
|
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
13731
14263
|
if (deps.jest) return "jest";
|
|
13732
14264
|
if (deps.vitest) return "vitest";
|
|
@@ -13735,7 +14267,7 @@ function getTestFramework(dir) {
|
|
|
13735
14267
|
if (deps["@playwright/test"]) return "playwright";
|
|
13736
14268
|
if (deps.cypress) return "cypress";
|
|
13737
14269
|
}
|
|
13738
|
-
if (
|
|
14270
|
+
if (fs35.existsSync(path37.join(dir, "pytest.ini")) || fs35.existsSync(path37.join(dir, "conftest.py"))) return "pytest";
|
|
13739
14271
|
return "unknown";
|
|
13740
14272
|
} catch {
|
|
13741
14273
|
return "unknown";
|
|
@@ -13743,9 +14275,9 @@ function getTestFramework(dir) {
|
|
|
13743
14275
|
}
|
|
13744
14276
|
function getTestCommand(dir) {
|
|
13745
14277
|
try {
|
|
13746
|
-
const pkgPath =
|
|
13747
|
-
if (
|
|
13748
|
-
const pkg = JSON.parse(
|
|
14278
|
+
const pkgPath = path37.join(dir, "package.json");
|
|
14279
|
+
if (fs35.existsSync(pkgPath)) {
|
|
14280
|
+
const pkg = JSON.parse(fs35.readFileSync(pkgPath, "utf-8"));
|
|
13749
14281
|
if (pkg.scripts?.test) return `npm test`;
|
|
13750
14282
|
if (pkg.scripts?.["test:unit"]) return `npm run test:unit`;
|
|
13751
14283
|
}
|
|
@@ -13937,7 +14469,15 @@ async function getUnifiedSystemPrompt(availableSkills, options) {
|
|
|
13937
14469
|
const fromAgent = process.env.BLUMA_FROM_AGENT || "severino";
|
|
13938
14470
|
const action = process.env.BLUMA_ACTION || "unknown";
|
|
13939
14471
|
const sessionId = process.env.BLUMA_SESSION_ID || "unknown";
|
|
14472
|
+
const factorAiAppContext = loadFactorAiAppContext();
|
|
13940
14473
|
prompt = prompt.replaceAll("{from_agent}", fromAgent).replaceAll("{action}", action).replaceAll("{session_id}", sessionId).replaceAll("{workspace_root}", env.workdir);
|
|
14474
|
+
if (factorAiAppContext) {
|
|
14475
|
+
prompt += `
|
|
14476
|
+
|
|
14477
|
+
<factorai_app_context>
|
|
14478
|
+
${formatFactorAiAppContextSummary(factorAiAppContext)}
|
|
14479
|
+
</factorai_app_context>`;
|
|
14480
|
+
}
|
|
13941
14481
|
}
|
|
13942
14482
|
prompt += buildOutputStylePrompt(runtimeConfig.outputStyle);
|
|
13943
14483
|
prompt += buildPermissionModePrompt(runtimeConfig.permissionMode);
|
|
@@ -14037,8 +14577,8 @@ ${blumaMdContent}
|
|
|
14037
14577
|
}
|
|
14038
14578
|
function isGitRepo(dir) {
|
|
14039
14579
|
try {
|
|
14040
|
-
const gitPath =
|
|
14041
|
-
return
|
|
14580
|
+
const gitPath = path37.join(dir, ".git");
|
|
14581
|
+
return fs35.existsSync(gitPath) && fs35.lstatSync(gitPath).isDirectory();
|
|
14042
14582
|
} catch {
|
|
14043
14583
|
return false;
|
|
14044
14584
|
}
|
|
@@ -14246,7 +14786,8 @@ function defaultBlumaUserContextInput(sessionId, userMessage) {
|
|
|
14246
14786
|
userName: null,
|
|
14247
14787
|
userEmail: null,
|
|
14248
14788
|
companyId: null,
|
|
14249
|
-
companyName: null
|
|
14789
|
+
companyName: null,
|
|
14790
|
+
appContext: loadFactorAiAppContext()
|
|
14250
14791
|
};
|
|
14251
14792
|
}
|
|
14252
14793
|
function getPreferredMacAddress() {
|
|
@@ -14784,11 +15325,11 @@ function effectiveToolAutoApprove(toolCall, sessionId, options) {
|
|
|
14784
15325
|
}
|
|
14785
15326
|
|
|
14786
15327
|
// src/app/agent/tools/natives/coding_memory_consolidate.ts
|
|
14787
|
-
import * as
|
|
14788
|
-
import * as
|
|
15328
|
+
import * as fs36 from "fs";
|
|
15329
|
+
import * as path38 from "path";
|
|
14789
15330
|
import os24 from "os";
|
|
14790
15331
|
function memoryPath2() {
|
|
14791
|
-
return
|
|
15332
|
+
return path38.join(process.env.HOME || os24.homedir(), ".bluma", "coding_memory.json");
|
|
14792
15333
|
}
|
|
14793
15334
|
function normalizeNote2(note) {
|
|
14794
15335
|
return note.trim().toLowerCase().replace(/\s+/g, " ");
|
|
@@ -14798,18 +15339,18 @@ function uniqTags(a, b) {
|
|
|
14798
15339
|
}
|
|
14799
15340
|
function consolidateCodingMemoryFile() {
|
|
14800
15341
|
const p = memoryPath2();
|
|
14801
|
-
if (!
|
|
15342
|
+
if (!fs36.existsSync(p)) {
|
|
14802
15343
|
return { success: true, removedDuplicates: 0, message: "no coding_memory.json" };
|
|
14803
15344
|
}
|
|
14804
15345
|
const bak = `${p}.bak`;
|
|
14805
15346
|
try {
|
|
14806
|
-
|
|
15347
|
+
fs36.copyFileSync(p, bak);
|
|
14807
15348
|
} catch (e) {
|
|
14808
15349
|
return { success: false, removedDuplicates: 0, message: `backup failed: ${e.message}` };
|
|
14809
15350
|
}
|
|
14810
15351
|
let data;
|
|
14811
15352
|
try {
|
|
14812
|
-
data = JSON.parse(
|
|
15353
|
+
data = JSON.parse(fs36.readFileSync(p, "utf-8"));
|
|
14813
15354
|
} catch (e) {
|
|
14814
15355
|
return { success: false, removedDuplicates: 0, message: `invalid json: ${e.message}` };
|
|
14815
15356
|
}
|
|
@@ -14844,7 +15385,7 @@ function consolidateCodingMemoryFile() {
|
|
|
14844
15385
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
14845
15386
|
};
|
|
14846
15387
|
try {
|
|
14847
|
-
|
|
15388
|
+
fs36.writeFileSync(p, JSON.stringify(out, null, 2), "utf-8");
|
|
14848
15389
|
} catch (e) {
|
|
14849
15390
|
return { success: false, removedDuplicates: 0, message: `write failed: ${e.message}` };
|
|
14850
15391
|
}
|
|
@@ -14887,7 +15428,8 @@ function buildTurnStartBackendMessage(params) {
|
|
|
14887
15428
|
type: "turn_start",
|
|
14888
15429
|
turnId: params.turnId,
|
|
14889
15430
|
sessionId: params.sessionId,
|
|
14890
|
-
userPromptPreview: buildUserPromptPreview(params.inputText)
|
|
15431
|
+
userPromptPreview: buildUserPromptPreview(params.inputText),
|
|
15432
|
+
appContext: params.appContext ?? null
|
|
14891
15433
|
};
|
|
14892
15434
|
}
|
|
14893
15435
|
|
|
@@ -15069,7 +15611,8 @@ var BluMaAgent = class {
|
|
|
15069
15611
|
buildTurnStartBackendMessage({
|
|
15070
15612
|
turnId: this.activeTurnContext.turnId,
|
|
15071
15613
|
sessionId: this.activeTurnContext.sessionId,
|
|
15072
|
-
inputText
|
|
15614
|
+
inputText,
|
|
15615
|
+
appContext: this.activeTurnContext.appContext ?? null
|
|
15073
15616
|
})
|
|
15074
15617
|
);
|
|
15075
15618
|
if (inputText === "/init") {
|
|
@@ -15449,7 +15992,7 @@ var BluMaAgent = class {
|
|
|
15449
15992
|
|
|
15450
15993
|
${editData.error.display}`;
|
|
15451
15994
|
}
|
|
15452
|
-
const filename =
|
|
15995
|
+
const filename = path39.basename(toolArgs.file_path);
|
|
15453
15996
|
return createDiff(filename, editData.currentContent || "", editData.newContent);
|
|
15454
15997
|
} catch (e) {
|
|
15455
15998
|
return `An unexpected error occurred while generating the edit preview: ${e.message}`;
|
|
@@ -16425,16 +16968,16 @@ async function fullCompact(messages, targetTokens, summarizer, llmClient) {
|
|
|
16425
16968
|
}
|
|
16426
16969
|
|
|
16427
16970
|
// src/app/agent/core/memory/session_memory.ts
|
|
16428
|
-
import
|
|
16971
|
+
import fs37 from "fs";
|
|
16429
16972
|
import os27 from "os";
|
|
16430
|
-
import
|
|
16973
|
+
import path40 from "path";
|
|
16431
16974
|
import { v4 as uuidv49 } from "uuid";
|
|
16432
16975
|
var SessionMemoryExtractor = class {
|
|
16433
16976
|
llmClient;
|
|
16434
16977
|
memoryFile;
|
|
16435
16978
|
constructor(options = {}) {
|
|
16436
16979
|
this.llmClient = options.llmClient;
|
|
16437
|
-
this.memoryFile = options.memoryFile ||
|
|
16980
|
+
this.memoryFile = options.memoryFile || path40.join(os27.homedir(), ".bluma", "session_memory.json");
|
|
16438
16981
|
}
|
|
16439
16982
|
/**
|
|
16440
16983
|
* Extract memories from conversation using LLM
|
|
@@ -16491,15 +17034,15 @@ ${messages.slice(-50).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("
|
|
|
16491
17034
|
);
|
|
16492
17035
|
unique.sort((a, b) => b.accessCount - a.accessCount);
|
|
16493
17036
|
const trimmed = unique.slice(0, 200);
|
|
16494
|
-
|
|
17037
|
+
fs37.writeFileSync(this.memoryFile, JSON.stringify(trimmed, null, 2));
|
|
16495
17038
|
}
|
|
16496
17039
|
/**
|
|
16497
17040
|
* Load memories from disk
|
|
16498
17041
|
*/
|
|
16499
17042
|
async loadMemories() {
|
|
16500
17043
|
try {
|
|
16501
|
-
if (!
|
|
16502
|
-
const data =
|
|
17044
|
+
if (!fs37.existsSync(this.memoryFile)) return [];
|
|
17045
|
+
const data = fs37.readFileSync(this.memoryFile, "utf-8");
|
|
16503
17046
|
return JSON.parse(data);
|
|
16504
17047
|
} catch {
|
|
16505
17048
|
return [];
|
|
@@ -17036,14 +17579,14 @@ var RouteManager = class {
|
|
|
17036
17579
|
this.subAgents = subAgents;
|
|
17037
17580
|
this.core = core;
|
|
17038
17581
|
}
|
|
17039
|
-
registerRoute(
|
|
17040
|
-
this.routeHandlers.set(
|
|
17582
|
+
registerRoute(path45, handler) {
|
|
17583
|
+
this.routeHandlers.set(path45, handler);
|
|
17041
17584
|
}
|
|
17042
17585
|
async handleRoute(payload) {
|
|
17043
17586
|
const inputText = String(payload.content || "").trim();
|
|
17044
17587
|
const { userContext } = payload;
|
|
17045
|
-
for (const [
|
|
17046
|
-
if (inputText ===
|
|
17588
|
+
for (const [path45, handler] of this.routeHandlers) {
|
|
17589
|
+
if (inputText === path45 || inputText.startsWith(`${path45} `)) {
|
|
17047
17590
|
return handler({ content: inputText, userContext });
|
|
17048
17591
|
}
|
|
17049
17592
|
}
|
|
@@ -17052,13 +17595,13 @@ var RouteManager = class {
|
|
|
17052
17595
|
};
|
|
17053
17596
|
|
|
17054
17597
|
// src/app/agent/runtime/plugin_runtime.ts
|
|
17055
|
-
import
|
|
17598
|
+
import path41 from "path";
|
|
17056
17599
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
17057
17600
|
async function loadPluginsAtStartup() {
|
|
17058
17601
|
for (const p of listPlugins()) {
|
|
17059
17602
|
const entry = p.manifest.entry?.trim();
|
|
17060
17603
|
if (!entry) continue;
|
|
17061
|
-
const abs =
|
|
17604
|
+
const abs = path41.resolve(p.root, entry);
|
|
17062
17605
|
try {
|
|
17063
17606
|
const href = pathToFileURL2(abs).href;
|
|
17064
17607
|
const mod = await import(href);
|
|
@@ -17079,7 +17622,7 @@ async function loadPluginsAtStartup() {
|
|
|
17079
17622
|
}
|
|
17080
17623
|
|
|
17081
17624
|
// src/app/agent/agent.ts
|
|
17082
|
-
var globalEnvPath =
|
|
17625
|
+
var globalEnvPath = path42.join(os28.homedir(), ".bluma", ".env");
|
|
17083
17626
|
dotenv.config({ path: globalEnvPath });
|
|
17084
17627
|
var Agent = class {
|
|
17085
17628
|
sessionId;
|
|
@@ -17091,9 +17634,11 @@ var Agent = class {
|
|
|
17091
17634
|
core;
|
|
17092
17635
|
subAgents;
|
|
17093
17636
|
toolInvoker;
|
|
17637
|
+
factorAiAppContext;
|
|
17094
17638
|
constructor(sessionId, eventBus) {
|
|
17095
17639
|
this.sessionId = sessionId;
|
|
17096
17640
|
this.eventBus = eventBus;
|
|
17641
|
+
this.factorAiAppContext = loadFactorAiAppContext();
|
|
17097
17642
|
const nativeToolInvoker = new ToolInvoker();
|
|
17098
17643
|
this.toolInvoker = nativeToolInvoker;
|
|
17099
17644
|
this.mcpClient = new MCPClient(nativeToolInvoker, eventBus);
|
|
@@ -17209,6 +17754,9 @@ var Agent = class {
|
|
|
17209
17754
|
async processTurn(userInput, userContextInput) {
|
|
17210
17755
|
const inputText = String(userInput.content || "").trim();
|
|
17211
17756
|
const resolvedUserContext = userContextInput ?? defaultInteractiveCliUserContextInput(this.sessionId, inputText.slice(0, 300));
|
|
17757
|
+
if (this.factorAiAppContext && !resolvedUserContext.appContext) {
|
|
17758
|
+
resolvedUserContext.appContext = this.factorAiAppContext;
|
|
17759
|
+
}
|
|
17212
17760
|
if (inputText === "/init" || inputText.startsWith("/init ")) {
|
|
17213
17761
|
this.routeManager.registerRoute("/init", this.dispatchToSubAgent);
|
|
17214
17762
|
}
|
|
@@ -21660,16 +22208,16 @@ import latestVersion from "latest-version";
|
|
|
21660
22208
|
import semverGt from "semver/functions/gt.js";
|
|
21661
22209
|
import semverValid from "semver/functions/valid.js";
|
|
21662
22210
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21663
|
-
import
|
|
21664
|
-
import
|
|
22211
|
+
import path43 from "path";
|
|
22212
|
+
import fs38 from "fs";
|
|
21665
22213
|
var BLUMA_PACKAGE_NAME = "@nomad-e/bluma-cli";
|
|
21666
22214
|
function findBlumaPackageJson(startDir) {
|
|
21667
22215
|
let dir = startDir;
|
|
21668
22216
|
for (let i = 0; i < 12; i++) {
|
|
21669
|
-
const candidate =
|
|
21670
|
-
if (
|
|
22217
|
+
const candidate = path43.join(dir, "package.json");
|
|
22218
|
+
if (fs38.existsSync(candidate)) {
|
|
21671
22219
|
try {
|
|
21672
|
-
const raw =
|
|
22220
|
+
const raw = fs38.readFileSync(candidate, "utf8");
|
|
21673
22221
|
const parsed = JSON.parse(raw);
|
|
21674
22222
|
if (parsed?.name === BLUMA_PACKAGE_NAME && parsed?.version) {
|
|
21675
22223
|
return { name: parsed.name, version: String(parsed.version) };
|
|
@@ -21677,7 +22225,7 @@ function findBlumaPackageJson(startDir) {
|
|
|
21677
22225
|
} catch {
|
|
21678
22226
|
}
|
|
21679
22227
|
}
|
|
21680
|
-
const parent =
|
|
22228
|
+
const parent = path43.dirname(dir);
|
|
21681
22229
|
if (parent === dir) break;
|
|
21682
22230
|
dir = parent;
|
|
21683
22231
|
}
|
|
@@ -21686,13 +22234,13 @@ function findBlumaPackageJson(startDir) {
|
|
|
21686
22234
|
function resolveInstalledBlumaPackage() {
|
|
21687
22235
|
const tried = /* @__PURE__ */ new Set();
|
|
21688
22236
|
const tryFrom = (dir) => {
|
|
21689
|
-
const abs =
|
|
22237
|
+
const abs = path43.resolve(dir);
|
|
21690
22238
|
if (tried.has(abs)) return null;
|
|
21691
22239
|
tried.add(abs);
|
|
21692
22240
|
return findBlumaPackageJson(abs);
|
|
21693
22241
|
};
|
|
21694
22242
|
try {
|
|
21695
|
-
const fromBundle = tryFrom(
|
|
22243
|
+
const fromBundle = tryFrom(path43.dirname(fileURLToPath5(import.meta.url)));
|
|
21696
22244
|
if (fromBundle) return fromBundle;
|
|
21697
22245
|
} catch {
|
|
21698
22246
|
}
|
|
@@ -21700,12 +22248,12 @@ function resolveInstalledBlumaPackage() {
|
|
|
21700
22248
|
if (argv1 && !argv1.startsWith("-")) {
|
|
21701
22249
|
try {
|
|
21702
22250
|
let resolved = argv1;
|
|
21703
|
-
if (
|
|
21704
|
-
resolved =
|
|
22251
|
+
if (path43.isAbsolute(argv1) && fs38.existsSync(argv1)) {
|
|
22252
|
+
resolved = fs38.realpathSync(argv1);
|
|
21705
22253
|
} else {
|
|
21706
|
-
resolved =
|
|
22254
|
+
resolved = path43.resolve(process.cwd(), argv1);
|
|
21707
22255
|
}
|
|
21708
|
-
const fromArgv = tryFrom(
|
|
22256
|
+
const fromArgv = tryFrom(path43.dirname(resolved));
|
|
21709
22257
|
if (fromArgv) return fromArgv;
|
|
21710
22258
|
} catch {
|
|
21711
22259
|
}
|
|
@@ -23442,9 +23990,9 @@ async function runAgentMode() {
|
|
|
23442
23990
|
try {
|
|
23443
23991
|
if (inputFileIndex !== -1 && args[inputFileIndex + 1]) {
|
|
23444
23992
|
const filePath = args[inputFileIndex + 1];
|
|
23445
|
-
rawPayload =
|
|
23993
|
+
rawPayload = fs39.readFileSync(filePath, "utf-8");
|
|
23446
23994
|
} else {
|
|
23447
|
-
rawPayload =
|
|
23995
|
+
rawPayload = fs39.readFileSync(0, "utf-8");
|
|
23448
23996
|
}
|
|
23449
23997
|
} catch (err) {
|
|
23450
23998
|
writeAgentEvent(registrySessionId, {
|
|
@@ -23642,9 +24190,9 @@ async function runAgentMode() {
|
|
|
23642
24190
|
}
|
|
23643
24191
|
function readCliPackageVersion() {
|
|
23644
24192
|
try {
|
|
23645
|
-
const base =
|
|
23646
|
-
const pkgPath =
|
|
23647
|
-
const j = JSON.parse(
|
|
24193
|
+
const base = path44.dirname(fileURLToPath6(import.meta.url));
|
|
24194
|
+
const pkgPath = path44.join(base, "..", "package.json");
|
|
24195
|
+
const j = JSON.parse(fs39.readFileSync(pkgPath, "utf8"));
|
|
23648
24196
|
return String(j.version || "0.0.0");
|
|
23649
24197
|
} catch {
|
|
23650
24198
|
return "0.0.0";
|
|
@@ -23767,7 +24315,7 @@ function startBackgroundAgent() {
|
|
|
23767
24315
|
process.exit(1);
|
|
23768
24316
|
}
|
|
23769
24317
|
const filePath = args[inputFileIndex + 1];
|
|
23770
|
-
const rawPayload =
|
|
24318
|
+
const rawPayload = fs39.readFileSync(filePath, "utf-8");
|
|
23771
24319
|
const envelope = JSON.parse(rawPayload);
|
|
23772
24320
|
const sessionId = envelope.session_id || envelope.message_id || uuidv412();
|
|
23773
24321
|
registerSession({
|