@nomad-e/bluma-cli 0.17.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/skills/factorai-sh/SKILL.md +5 -5
- package/dist/main.js +201 -9
- package/package.json +1 -1
|
@@ -37,7 +37,7 @@ Always read `factorai.sh.json` in the project root before editing a deployed app
|
|
|
37
37
|
2. edit_tool / file_write under ./my-app/ using shadcn components + semantic Tailwind (bg-background, etc.)
|
|
38
38
|
3. Avoid npm run build before deploy (.next inflates ZIP); optional npx tsc --noEmit
|
|
39
39
|
4. factorai.sh.deploy_app({ projectDir: "./my-app", name: "my-app" })
|
|
40
|
-
5.
|
|
40
|
+
5. factorai.sh.get_app_status({ appId, wait: true }) until ready or failed (see Polling)
|
|
41
41
|
6. message(result) with factor-sh-url-app from manifest after ready
|
|
42
42
|
```
|
|
43
43
|
|
|
@@ -53,7 +53,7 @@ Always read `factorai.sh.json` in the project root before editing a deployed app
|
|
|
53
53
|
],
|
|
54
54
|
deploy: true,
|
|
55
55
|
})
|
|
56
|
-
4.
|
|
56
|
+
4. get_app_status({ appId, wait: true }) until ready or failed
|
|
57
57
|
5. message(result) with factor-sh-url-app from manifest after ready
|
|
58
58
|
```
|
|
59
59
|
|
|
@@ -67,9 +67,9 @@ Always read `factorai.sh.json` in the project root before editing a deployed app
|
|
|
67
67
|
|
|
68
68
|
## Polling checklist
|
|
69
69
|
|
|
70
|
-
1.
|
|
71
|
-
2.
|
|
72
|
-
3.
|
|
70
|
+
1. Prefer `factorai.sh.get_app_status({ appId, wait: true })` — blocks until `ready` or `failed`
|
|
71
|
+
2. On `failed`: read `data.typescriptErrors`, `data.buildLogTail`, `data.summary` — fix code, retry apply/deploy
|
|
72
|
+
3. Do not report live URL until `data.success === true` and `data.status === "ready"`
|
|
73
73
|
4. Typical rebuild: 30–90 seconds
|
|
74
74
|
|
|
75
75
|
## Tool reference
|
package/dist/main.js
CHANGED
|
@@ -20269,7 +20269,7 @@ async function uploadToSeverino(zipPath, severinoUrl, name, apiKey, appId) {
|
|
|
20269
20269
|
name: data.name,
|
|
20270
20270
|
status: data.status || "building",
|
|
20271
20271
|
url: appContext.appUrl || severinoUrl.replace(/\/$/, "") + `/app/${resolvedAppId}`,
|
|
20272
|
-
message: data.message || "Deploy iniciado",
|
|
20272
|
+
message: data.message || "Deploy iniciado. Chama factorai.sh.get_app_status({ appId, wait: true }) at\xE9 status ready ou failed; se failed, l\xEA typescriptErrors/buildLogTail e corrige o c\xF3digo.",
|
|
20273
20273
|
isRedeploy: !!appId,
|
|
20274
20274
|
appContext
|
|
20275
20275
|
};
|
|
@@ -20413,6 +20413,102 @@ async function deployApp(args) {
|
|
|
20413
20413
|
}
|
|
20414
20414
|
}
|
|
20415
20415
|
|
|
20416
|
+
// src/app/agent/runtime/factorai_deploy_feedback.ts
|
|
20417
|
+
var FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "build_failed"]);
|
|
20418
|
+
var READY_STATUSES = /* @__PURE__ */ new Set(["ready", "running", "live"]);
|
|
20419
|
+
var BUILDING_STATUSES = /* @__PURE__ */ new Set(["building", "deploying", "installing", "pending"]);
|
|
20420
|
+
function extractTypeScriptErrors(logText) {
|
|
20421
|
+
if (!logText.trim()) return [];
|
|
20422
|
+
const lines = logText.split("\n");
|
|
20423
|
+
const blocks = [];
|
|
20424
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20425
|
+
const line = lines[i];
|
|
20426
|
+
if (line.includes("Type error:") || line.includes("Failed to compile") || /^\s*>\s*\d+\s*\|/.test(line)) {
|
|
20427
|
+
const start = Math.max(0, i - 3);
|
|
20428
|
+
const end = Math.min(lines.length, i + 8);
|
|
20429
|
+
blocks.push(lines.slice(start, end).join("\n").trim());
|
|
20430
|
+
}
|
|
20431
|
+
}
|
|
20432
|
+
return [...new Set(blocks)].slice(0, 6);
|
|
20433
|
+
}
|
|
20434
|
+
function normalizeFactorAiAppStatus(raw, appId) {
|
|
20435
|
+
const envelope = raw && typeof raw === "object" ? raw : {};
|
|
20436
|
+
const data = envelope.data && typeof envelope.data === "object" && !Array.isArray(envelope.data) ? envelope.data : envelope;
|
|
20437
|
+
const status = String(data.status ?? "unknown").toLowerCase();
|
|
20438
|
+
const logs = typeof data.logs === "string" ? data.logs : "";
|
|
20439
|
+
const logTail = logs.length > 12e3 ? logs.slice(-12e3) : logs;
|
|
20440
|
+
const lastError = typeof data.lastError === "string" && data.lastError || typeof data.error === "string" && data.error || void 0;
|
|
20441
|
+
const slug = typeof data.slug === "string" ? data.slug : void 0;
|
|
20442
|
+
const url = typeof data.url === "string" ? data.url : void 0;
|
|
20443
|
+
const tsErrors = extractTypeScriptErrors(logTail);
|
|
20444
|
+
if (FAILED_STATUSES.has(status) || lastError) {
|
|
20445
|
+
const error = lastError || tsErrors[0] || (logTail.includes("next build falhou") ? logTail.split("\n").slice(-5).join("\n") : "") || "Deploy/build failed on FactorAI.sh";
|
|
20446
|
+
return {
|
|
20447
|
+
success: false,
|
|
20448
|
+
terminal: true,
|
|
20449
|
+
status: "failed",
|
|
20450
|
+
appId,
|
|
20451
|
+
slug,
|
|
20452
|
+
url,
|
|
20453
|
+
error,
|
|
20454
|
+
buildLogTail: logTail || void 0,
|
|
20455
|
+
typescriptErrors: tsErrors.length > 0 ? tsErrors : void 0,
|
|
20456
|
+
agentGuidance: "O build falhou no servidor. Corrige os erros TypeScript/compila\xE7\xE3o no projeto local (ex.: components/ui/card.tsx), confirma com npx tsc --noEmit se poss\xEDvel, depois factorai.sh.apply_app_changes ou deploy_app. N\xE3o digas ao utilizador que a app est\xE1 online."
|
|
20457
|
+
};
|
|
20458
|
+
}
|
|
20459
|
+
if (READY_STATUSES.has(status)) {
|
|
20460
|
+
return {
|
|
20461
|
+
success: true,
|
|
20462
|
+
terminal: true,
|
|
20463
|
+
status: "ready",
|
|
20464
|
+
appId,
|
|
20465
|
+
slug,
|
|
20466
|
+
url,
|
|
20467
|
+
buildLogTail: logTail || void 0
|
|
20468
|
+
};
|
|
20469
|
+
}
|
|
20470
|
+
if (BUILDING_STATUSES.has(status)) {
|
|
20471
|
+
return {
|
|
20472
|
+
success: true,
|
|
20473
|
+
terminal: false,
|
|
20474
|
+
status,
|
|
20475
|
+
appId,
|
|
20476
|
+
slug,
|
|
20477
|
+
url,
|
|
20478
|
+
buildLogTail: logTail || void 0
|
|
20479
|
+
};
|
|
20480
|
+
}
|
|
20481
|
+
return {
|
|
20482
|
+
success: true,
|
|
20483
|
+
terminal: false,
|
|
20484
|
+
status,
|
|
20485
|
+
appId,
|
|
20486
|
+
slug,
|
|
20487
|
+
url,
|
|
20488
|
+
buildLogTail: logTail || void 0
|
|
20489
|
+
};
|
|
20490
|
+
}
|
|
20491
|
+
function formatFactorAiDeployStatusForAgent(result) {
|
|
20492
|
+
const lines = [`status=${result.status}`, `appId=${result.appId}`];
|
|
20493
|
+
if (result.slug) lines.push(`slug=${result.slug}`);
|
|
20494
|
+
if (result.url) lines.push(`url=${result.url}`);
|
|
20495
|
+
if (result.error) lines.push(`error=${result.error}`);
|
|
20496
|
+
if (result.typescriptErrors?.length) {
|
|
20497
|
+
lines.push("typescriptErrors:");
|
|
20498
|
+
for (const block of result.typescriptErrors) {
|
|
20499
|
+
lines.push(block);
|
|
20500
|
+
}
|
|
20501
|
+
} else if (result.buildLogTail) {
|
|
20502
|
+
lines.push("buildLogTail:");
|
|
20503
|
+
lines.push(result.buildLogTail);
|
|
20504
|
+
}
|
|
20505
|
+
if (result.agentGuidance) lines.push(`guidance=${result.agentGuidance}`);
|
|
20506
|
+
return lines.join("\n");
|
|
20507
|
+
}
|
|
20508
|
+
function sleep(ms) {
|
|
20509
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
20510
|
+
}
|
|
20511
|
+
|
|
20416
20512
|
// src/app/agent/runtime/native_tool_catalog.ts
|
|
20417
20513
|
init_sandbox_policy();
|
|
20418
20514
|
function getFactorAiBaseUrl() {
|
|
@@ -20494,9 +20590,81 @@ async function resolveFactorAiAppId(explicitAppId) {
|
|
|
20494
20590
|
"appId em falta: passa o UUID de factorai.sh.json (appContext.appId) ou faz deploy_app primeiro nesta sess\xE3o."
|
|
20495
20591
|
);
|
|
20496
20592
|
}
|
|
20593
|
+
function isFactorAiNotFoundError(err) {
|
|
20594
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
20595
|
+
return /not found/i.test(msg) || /404/.test(msg);
|
|
20596
|
+
}
|
|
20597
|
+
async function fetchAndNormalizeAppStatus(appId) {
|
|
20598
|
+
const payload = await requestFactorAi(`/api/v1/apps/${encodeURIComponent(appId)}`);
|
|
20599
|
+
return normalizeFactorAiAppStatus(payload, appId);
|
|
20600
|
+
}
|
|
20497
20601
|
async function factorAiGetAppStatus(args) {
|
|
20498
20602
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
20499
|
-
|
|
20603
|
+
const wait = args?.wait === true;
|
|
20604
|
+
const timeoutMs = Math.max(5, args?.timeoutSeconds ?? 480) * 1e3;
|
|
20605
|
+
const pollMs = Math.max(2, args?.pollIntervalSeconds ?? 5) * 1e3;
|
|
20606
|
+
const deadline = Date.now() + timeoutMs;
|
|
20607
|
+
const finalize = (result) => ({
|
|
20608
|
+
success: true,
|
|
20609
|
+
data: {
|
|
20610
|
+
...result,
|
|
20611
|
+
summary: formatFactorAiDeployStatusForAgent(result)
|
|
20612
|
+
}
|
|
20613
|
+
});
|
|
20614
|
+
if (!wait) {
|
|
20615
|
+
try {
|
|
20616
|
+
return finalize(await fetchAndNormalizeAppStatus(appId));
|
|
20617
|
+
} catch (err) {
|
|
20618
|
+
if (isFactorAiNotFoundError(err)) {
|
|
20619
|
+
return finalize({
|
|
20620
|
+
success: false,
|
|
20621
|
+
terminal: true,
|
|
20622
|
+
status: "failed",
|
|
20623
|
+
appId,
|
|
20624
|
+
error: String(err instanceof Error ? err.message : err),
|
|
20625
|
+
agentGuidance: "App n\xE3o encontrada no FactorAI.sh \u2014 o deploy provavelmente falhou e o registo foi limpo, ou o appId est\xE1 errado. L\xEA factorai.sh.json, confirma FACTORAI_BASE_URL=SEVERINO_URL, e faz deploy de novo ap\xF3s corrigir o c\xF3digo."
|
|
20626
|
+
});
|
|
20627
|
+
}
|
|
20628
|
+
throw err;
|
|
20629
|
+
}
|
|
20630
|
+
}
|
|
20631
|
+
let sawBuilding = false;
|
|
20632
|
+
while (Date.now() < deadline) {
|
|
20633
|
+
try {
|
|
20634
|
+
const result = await fetchAndNormalizeAppStatus(appId);
|
|
20635
|
+
if (!result.terminal && result.status !== "failed") {
|
|
20636
|
+
sawBuilding = true;
|
|
20637
|
+
}
|
|
20638
|
+
if (result.terminal) {
|
|
20639
|
+
if (result.status === "ready" && !sawBuilding) {
|
|
20640
|
+
await sleep(pollMs);
|
|
20641
|
+
continue;
|
|
20642
|
+
}
|
|
20643
|
+
return finalize(result);
|
|
20644
|
+
}
|
|
20645
|
+
} catch (err) {
|
|
20646
|
+
if (isFactorAiNotFoundError(err)) {
|
|
20647
|
+
return finalize({
|
|
20648
|
+
success: false,
|
|
20649
|
+
terminal: true,
|
|
20650
|
+
status: "failed",
|
|
20651
|
+
appId,
|
|
20652
|
+
error: String(err instanceof Error ? err.message : err),
|
|
20653
|
+
agentGuidance: "Deploy falhou: a app desapareceu do registry (build error no servidor). Corrige o c\xF3digo e volta a deploy_app."
|
|
20654
|
+
});
|
|
20655
|
+
}
|
|
20656
|
+
throw err;
|
|
20657
|
+
}
|
|
20658
|
+
await sleep(pollMs);
|
|
20659
|
+
}
|
|
20660
|
+
return finalize({
|
|
20661
|
+
success: true,
|
|
20662
|
+
terminal: false,
|
|
20663
|
+
status: "building",
|
|
20664
|
+
appId,
|
|
20665
|
+
error: `Timeout ap\xF3s ${Math.round(timeoutMs / 1e3)}s \xE0 espera de ready/failed`,
|
|
20666
|
+
agentGuidance: "Continua a pollar get_app_status ou verifica logs no FactorAI.sh."
|
|
20667
|
+
});
|
|
20500
20668
|
}
|
|
20501
20669
|
async function factorAiApplyAppChanges(args) {
|
|
20502
20670
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
@@ -20512,7 +20680,13 @@ async function factorAiApplyAppChanges(args) {
|
|
|
20512
20680
|
})
|
|
20513
20681
|
});
|
|
20514
20682
|
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
20515
|
-
|
|
20683
|
+
const normalized = normalizeFactorAiAppStatus(response, appId);
|
|
20684
|
+
const summary = formatFactorAiDeployStatusForAgent(normalized);
|
|
20685
|
+
const message2 = args?.deploy !== false && !normalized.terminal ? "Changes accepted; build em curso. Chama factorai.sh.get_app_status({ wait: true }) at\xE9 ready ou failed." : normalized.success ? "Changes applied" : normalized.error;
|
|
20686
|
+
return {
|
|
20687
|
+
success: true,
|
|
20688
|
+
data: { ...normalized, summary, message: message2 }
|
|
20689
|
+
};
|
|
20516
20690
|
}
|
|
20517
20691
|
async function factorAiRedeployApp(args) {
|
|
20518
20692
|
const appId = await resolveFactorAiAppId(args?.appId);
|
|
@@ -20520,7 +20694,13 @@ async function factorAiRedeployApp(args) {
|
|
|
20520
20694
|
method: "POST"
|
|
20521
20695
|
});
|
|
20522
20696
|
await persistFactorAiWorkspaceManifestFromResponse(response);
|
|
20523
|
-
|
|
20697
|
+
const normalized = normalizeFactorAiAppStatus(response, appId);
|
|
20698
|
+
const summary = formatFactorAiDeployStatusForAgent(normalized);
|
|
20699
|
+
const message2 = normalized.terminal ? normalized.success ? "Redeploy complete" : normalized.error : "Redeploy started; poll get_app_status({ wait: true }).";
|
|
20700
|
+
return {
|
|
20701
|
+
success: true,
|
|
20702
|
+
data: { ...normalized, summary, message: message2 }
|
|
20703
|
+
};
|
|
20524
20704
|
}
|
|
20525
20705
|
function extractFactorAiResponseData(response) {
|
|
20526
20706
|
if (response && typeof response === "object" && response.data && typeof response.data === "object") {
|
|
@@ -20607,13 +20787,25 @@ function getFactorAiSandboxToolDefinitions() {
|
|
|
20607
20787
|
type: "function",
|
|
20608
20788
|
function: {
|
|
20609
20789
|
name: "factorai.sh.get_app_status",
|
|
20610
|
-
description: "Poll deploy/rebuild status
|
|
20790
|
+
description: "Poll deploy/rebuild status. On failed returns buildLogTail + typescriptErrors so you can fix code. Use wait:true after deploy_app/apply_app_changes until ready or failed before message(result).",
|
|
20611
20791
|
parameters: {
|
|
20612
20792
|
type: "object",
|
|
20613
20793
|
properties: {
|
|
20614
20794
|
appId: {
|
|
20615
20795
|
type: "string",
|
|
20616
20796
|
description: "UUID from factorai.sh.json (appContext.appId). Optional if manifest exists in cwd. Do not use URL slug."
|
|
20797
|
+
},
|
|
20798
|
+
wait: {
|
|
20799
|
+
type: "boolean",
|
|
20800
|
+
description: "If true, poll until ready/failed/timeout (recommended after deploy)."
|
|
20801
|
+
},
|
|
20802
|
+
timeoutSeconds: {
|
|
20803
|
+
type: "number",
|
|
20804
|
+
description: "Max wait time when wait=true (default 480)."
|
|
20805
|
+
},
|
|
20806
|
+
pollIntervalSeconds: {
|
|
20807
|
+
type: "number",
|
|
20808
|
+
description: "Seconds between polls when wait=true (default 5)."
|
|
20617
20809
|
}
|
|
20618
20810
|
},
|
|
20619
20811
|
required: [],
|
|
@@ -23791,11 +23983,11 @@ Use \`factorai.sh.redeploy_app({ appId })\` only when sources on the server are
|
|
|
23791
23983
|
|
|
23792
23984
|
## Polling after deploy / apply_app_changes
|
|
23793
23985
|
|
|
23794
|
-
1. \`factorai.sh.get_app_status({ appId })\` or
|
|
23986
|
+
1. **Always** \`factorai.sh.get_app_status({ appId, wait: true })\` after \`deploy_app\` or \`apply_app_changes\` (polls at\xE9 \`ready\` ou \`failed\`).
|
|
23795
23987
|
2. Expect \`status: "building"\` during \`npm install\` / \`next build\` (~30\u201390s).
|
|
23796
|
-
3. **Do not** report "live"
|
|
23797
|
-
4.
|
|
23798
|
-
5.
|
|
23988
|
+
3. **Do not** report "live" until \`success: true\` and \`status: "ready"\` no resultado do \`get_app_status\`.
|
|
23989
|
+
4. On \`success: false\` / \`status: "failed"\`: read \`typescriptErrors\`, \`buildLogTail\`, and \`summary\` from the tool result \u2014 fix the code like a human reading the Next build log, then \`apply_app_changes\` or \`deploy_app\` again.
|
|
23990
|
+
5. Never ignore deploy failures; never tell the user the app is online when \`get_app_status\` returned \`failed\`.
|
|
23799
23991
|
|
|
23800
23992
|
---
|
|
23801
23993
|
|