@mestreyoda/fabrica 0.1.8 → 0.1.9
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/index.js +136 -4
- package/dist/index.js.map +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -111329,8 +111329,8 @@ import fsSync from "node:fs";
|
|
|
111329
111329
|
import path5 from "node:path";
|
|
111330
111330
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
111331
111331
|
function getCurrentVersion() {
|
|
111332
|
-
if ("0.1.
|
|
111333
|
-
return "0.1.
|
|
111332
|
+
if ("0.1.9") {
|
|
111333
|
+
return "0.1.9";
|
|
111334
111334
|
}
|
|
111335
111335
|
try {
|
|
111336
111336
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -142032,6 +142032,9 @@ function registerAttachmentHook(api, ctx) {
|
|
|
142032
142032
|
|
|
142033
142033
|
// lib/dispatch/telegram-bootstrap-hook.ts
|
|
142034
142034
|
import { homedir as homedir3 } from "node:os";
|
|
142035
|
+
init_runtime_paths();
|
|
142036
|
+
init_extract_json();
|
|
142037
|
+
init_zod();
|
|
142035
142038
|
|
|
142036
142039
|
// lib/dispatch/telegram-bootstrap-session.ts
|
|
142037
142040
|
init_migrate_layout();
|
|
@@ -142070,7 +142073,7 @@ async function readTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
|
142070
142073
|
try {
|
|
142071
142074
|
const raw = await fs38.readFile(sessionPath(workspaceDir, conversationId), "utf-8");
|
|
142072
142075
|
const session = JSON.parse(raw);
|
|
142073
|
-
if (session.status === "clarifying" && Date.parse(session.suppressUntil) < Date.now()) {
|
|
142076
|
+
if ((session.status === "clarifying" || session.status === "classifying") && Date.parse(session.suppressUntil) < Date.now()) {
|
|
142074
142077
|
await fs38.unlink(sessionPath(workspaceDir, conversationId)).catch(() => {
|
|
142075
142078
|
});
|
|
142076
142079
|
return null;
|
|
@@ -142081,6 +142084,10 @@ async function readTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
|
142081
142084
|
throw error48;
|
|
142082
142085
|
}
|
|
142083
142086
|
}
|
|
142087
|
+
async function deleteTelegramBootstrapSession(workspaceDir, conversationId) {
|
|
142088
|
+
await fs38.unlink(sessionPath(workspaceDir, conversationId)).catch(() => {
|
|
142089
|
+
});
|
|
142090
|
+
}
|
|
142084
142091
|
async function writeTelegramBootstrapSession(workspaceDir, session) {
|
|
142085
142092
|
const dir = sessionsDir(workspaceDir);
|
|
142086
142093
|
await fs38.mkdir(dir, { recursive: true });
|
|
@@ -142184,6 +142191,56 @@ function parseBootstrapRequest(text) {
|
|
|
142184
142191
|
stackHint
|
|
142185
142192
|
};
|
|
142186
142193
|
}
|
|
142194
|
+
var MAX_CLASSIFY_LENGTH = 500;
|
|
142195
|
+
function isAmbiguousCandidate(text) {
|
|
142196
|
+
const lower2 = text.toLowerCase();
|
|
142197
|
+
if (lower2.length <= 20 || lower2.length > MAX_CLASSIFY_LENGTH) return false;
|
|
142198
|
+
const softwareCue = /\b(projeto|project|cli|api|app|aplicativo|servi[cç]o|library|biblioteca|repo|reposit[oó]rio|tool|ferramenta|sistema|system|bot|script|programa|program)\b/.test(lower2);
|
|
142199
|
+
return softwareCue;
|
|
142200
|
+
}
|
|
142201
|
+
var DmIntentSchema = external_exports.object({
|
|
142202
|
+
intent: external_exports.enum(["create_project", "other"]),
|
|
142203
|
+
confidence: external_exports.number().min(0).max(1),
|
|
142204
|
+
stackHint: external_exports.string().nullable().optional(),
|
|
142205
|
+
projectSlug: external_exports.string().nullable().optional()
|
|
142206
|
+
});
|
|
142207
|
+
var CLASSIFY_PROMPT_TEMPLATE = `Classify this Telegram DM. Is the user asking to create/build a new software project, or is it something else (question, greeting, status check)?
|
|
142208
|
+
|
|
142209
|
+
Message: "$CONTENT"
|
|
142210
|
+
|
|
142211
|
+
Return ONLY valid JSON:
|
|
142212
|
+
{"intent": "create_project" | "other", "confidence": 0.0-1.0, "stackHint": "<detected stack or null>", "projectSlug": "<suggested slug or null>"}
|
|
142213
|
+
|
|
142214
|
+
Examples:
|
|
142215
|
+
- "Cria uma CLI Python que valida CPF" \u2192 {"intent":"create_project","confidence":0.95,"stackHint":"python-cli","projectSlug":"validador-cpf-cli"}
|
|
142216
|
+
- "Build me a REST API for tasks" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":"fastapi","projectSlug":"task-api"}
|
|
142217
|
+
- "How's the project going?" \u2192 {"intent":"other","confidence":0.95,"stackHint":null,"projectSlug":null}
|
|
142218
|
+
- "Oi, tudo bem?" \u2192 {"intent":"other","confidence":0.99,"stackHint":null,"projectSlug":null}
|
|
142219
|
+
- "Me faz um app que converte temperaturas" \u2192 {"intent":"create_project","confidence":0.9,"stackHint":null,"projectSlug":"conversor-temperaturas"}`;
|
|
142220
|
+
async function classifyDmIntent(ctx, content, workspaceDir) {
|
|
142221
|
+
try {
|
|
142222
|
+
const truncated = content.slice(0, MAX_CLASSIFY_LENGTH);
|
|
142223
|
+
const prompt = CLASSIFY_PROMPT_TEMPLATE.replace("$CONTENT", truncated.replace(/"/g, '\\"'));
|
|
142224
|
+
const cliPath = resolveOpenClawCli({ homeDir: homedir3(), workspaceDir });
|
|
142225
|
+
const sessionId = `dm-classify-${Date.now()}`;
|
|
142226
|
+
const result = await ctx.runCommand(
|
|
142227
|
+
[cliPath, "agent", "--local", "-m", prompt, "--session-id", sessionId, "--json"],
|
|
142228
|
+
{ timeoutMs: 15e3 }
|
|
142229
|
+
);
|
|
142230
|
+
const stdout = result.stdout ?? "";
|
|
142231
|
+
if (!stdout.trim()) return null;
|
|
142232
|
+
const parsed = extractJsonFromStdout(stdout);
|
|
142233
|
+
if (!parsed) return null;
|
|
142234
|
+
const text = parsed?.payloads?.[0]?.text;
|
|
142235
|
+
const jsonStr = text ? text.replace(/^```(json)?/gm, "").replace(/```$/gm, "").trim() : JSON.stringify(parsed);
|
|
142236
|
+
const intentData = JSON.parse(jsonStr);
|
|
142237
|
+
const validated = DmIntentSchema.safeParse(intentData);
|
|
142238
|
+
if (!validated.success) return null;
|
|
142239
|
+
return validated.data;
|
|
142240
|
+
} catch {
|
|
142241
|
+
return null;
|
|
142242
|
+
}
|
|
142243
|
+
}
|
|
142187
142244
|
function isBootstrapCandidate(text) {
|
|
142188
142245
|
const lower2 = text.toLowerCase();
|
|
142189
142246
|
if (/^\s*(project name|nome do projeto|repository url|repo url|stack)\s*:/im.test(text)) return true;
|
|
@@ -142274,6 +142331,67 @@ function logBootstrapWarning(ctx, message) {
|
|
|
142274
142331
|
ctx.logger.info(message);
|
|
142275
142332
|
}
|
|
142276
142333
|
}
|
|
142334
|
+
async function classifyAndBootstrap(ctx, workspaceDir, conversationId, content) {
|
|
142335
|
+
const classification = await classifyDmIntent(ctx, content, workspaceDir);
|
|
142336
|
+
if (!classification || classification.intent !== "create_project" || classification.confidence < 0.7) {
|
|
142337
|
+
if (!classification) {
|
|
142338
|
+
logBootstrapWarning(ctx, `[telegram-bootstrap] LLM classify failed, falling back (conversation: ${conversationId})`);
|
|
142339
|
+
}
|
|
142340
|
+
await deleteTelegramBootstrapSession(workspaceDir, conversationId);
|
|
142341
|
+
return;
|
|
142342
|
+
}
|
|
142343
|
+
await sendTelegramText(ctx, conversationId, "Recebi! Vou analisar e come\xE7ar a montar o projeto...");
|
|
142344
|
+
const parsed = parseBootstrapRequest(content);
|
|
142345
|
+
if (classification.stackHint && !parsed.stackHint) {
|
|
142346
|
+
parsed.stackHint = classification.stackHint;
|
|
142347
|
+
}
|
|
142348
|
+
if (classification.projectSlug && !parsed.projectName) {
|
|
142349
|
+
parsed.projectName = classification.projectSlug;
|
|
142350
|
+
}
|
|
142351
|
+
const incomingRequest = {
|
|
142352
|
+
rawIdea: parsed.rawIdea,
|
|
142353
|
+
projectName: parsed.projectName ?? null,
|
|
142354
|
+
stackHint: parsed.stackHint ?? null,
|
|
142355
|
+
repoUrl: parsed.repoUrl ?? null,
|
|
142356
|
+
repoPath: parsed.repoPath ?? null
|
|
142357
|
+
};
|
|
142358
|
+
const incomingRequestHash = buildBootstrapRequestHash(incomingRequest);
|
|
142359
|
+
const sessionForHash = await readTelegramBootstrapSession(workspaceDir, conversationId);
|
|
142360
|
+
if (sessionForHash?.requestHash === incomingRequestHash) {
|
|
142361
|
+
if (sessionForHash.status === "completed") {
|
|
142362
|
+
ctx.logger.info(`[telegram-bootstrap] duplicate completed DM ignored (LLM path) for conversation ${conversationId}`);
|
|
142363
|
+
return;
|
|
142364
|
+
}
|
|
142365
|
+
const isExpiredReceived = sessionForHash.status === "received" && Date.parse(sessionForHash.suppressUntil) < Date.now();
|
|
142366
|
+
if (sessionForHash.status !== "failed" && sessionForHash.status !== "classifying" && !isExpiredReceived) {
|
|
142367
|
+
ctx.logger.info(`[telegram-bootstrap] duplicate in-flight DM ignored (LLM path) for conversation ${conversationId}`);
|
|
142368
|
+
return;
|
|
142369
|
+
}
|
|
142370
|
+
}
|
|
142371
|
+
const sourceRoute = { channel: "telegram", channelId: conversationId };
|
|
142372
|
+
const session = await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142373
|
+
conversationId,
|
|
142374
|
+
...incomingRequest,
|
|
142375
|
+
sourceRoute,
|
|
142376
|
+
sourceChannel: "telegram",
|
|
142377
|
+
status: "received"
|
|
142378
|
+
});
|
|
142379
|
+
if (!parsed.stackHint) {
|
|
142380
|
+
const pendingClarification = !parsed.projectName ? "stack_and_name" : "stack";
|
|
142381
|
+
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142382
|
+
conversationId,
|
|
142383
|
+
...incomingRequest,
|
|
142384
|
+
sourceRoute: session.sourceRoute,
|
|
142385
|
+
status: "clarifying",
|
|
142386
|
+
pendingClarification
|
|
142387
|
+
});
|
|
142388
|
+
await sendTelegramText(ctx, conversationId, buildClarificationMessage(parsed, pendingClarification));
|
|
142389
|
+
return;
|
|
142390
|
+
}
|
|
142391
|
+
continueBootstrap(ctx, conversationId, workspaceDir, incomingRequest, sourceRoute).catch((err) => {
|
|
142392
|
+
logBootstrapWarning(ctx, `[telegram-bootstrap] unhandled pipeline error (LLM path): ${err instanceof Error ? err.message : String(err)}`);
|
|
142393
|
+
});
|
|
142394
|
+
}
|
|
142277
142395
|
async function continueBootstrap(ctx, conversationId, workspaceDir, request2, sourceRoute) {
|
|
142278
142396
|
const telegramConfig = readFabricaTelegramConfig(ctx.pluginConfig);
|
|
142279
142397
|
if (!telegramConfig.projectsForumChatId) {
|
|
@@ -142518,7 +142636,20 @@ function registerTelegramBootstrapHook(api, ctx) {
|
|
|
142518
142636
|
});
|
|
142519
142637
|
return;
|
|
142520
142638
|
}
|
|
142521
|
-
if (!isBootstrapCandidate(content))
|
|
142639
|
+
if (!isBootstrapCandidate(content)) {
|
|
142640
|
+
if (isAmbiguousCandidate(content)) {
|
|
142641
|
+
await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142642
|
+
conversationId,
|
|
142643
|
+
rawIdea: content,
|
|
142644
|
+
sourceRoute: { channel: "telegram", channelId: conversationId },
|
|
142645
|
+
status: "classifying"
|
|
142646
|
+
});
|
|
142647
|
+
classifyAndBootstrap(ctx, workspaceDir, conversationId, content).catch((err) => {
|
|
142648
|
+
logBootstrapWarning(ctx, `[telegram-bootstrap] LLM classify error: ${err instanceof Error ? err.message : String(err)}`);
|
|
142649
|
+
});
|
|
142650
|
+
}
|
|
142651
|
+
return;
|
|
142652
|
+
}
|
|
142522
142653
|
const parsed = parseBootstrapRequest(content);
|
|
142523
142654
|
const incomingRequest = {
|
|
142524
142655
|
rawIdea: parsed.rawIdea,
|
|
@@ -142543,6 +142674,7 @@ function registerTelegramBootstrapHook(api, ctx) {
|
|
|
142543
142674
|
ctx.logger.info(`[telegram-bootstrap] stale received session (expired) \u2014 restarting pipeline for conversation ${conversationId}`);
|
|
142544
142675
|
}
|
|
142545
142676
|
}
|
|
142677
|
+
await sendTelegramText(ctx, conversationId, "Recebi! Vou analisar e come\xE7ar a montar o projeto...");
|
|
142546
142678
|
const session = await upsertTelegramBootstrapSession(workspaceDir, {
|
|
142547
142679
|
conversationId,
|
|
142548
142680
|
...incomingRequest,
|