beplus-mcp 0.28.0 → 0.29.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/README.md +37 -5
- package/dist/index.js +650 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23004,7 +23004,7 @@ function probeWebp(b) {
|
|
|
23004
23004
|
}
|
|
23005
23005
|
function mp4Info(b) {
|
|
23006
23006
|
let duration3;
|
|
23007
|
-
let
|
|
23007
|
+
let width2 = 0;
|
|
23008
23008
|
let height = 0;
|
|
23009
23009
|
let frames = 0;
|
|
23010
23010
|
let mediaDuration;
|
|
@@ -23046,8 +23046,8 @@ function mp4Info(b) {
|
|
|
23046
23046
|
const rotated = Math.abs(a) < 0.01 && Math.abs(d) < 0.01;
|
|
23047
23047
|
const tw = Math.round(rotated ? h : w);
|
|
23048
23048
|
const th = Math.round(rotated ? w : h);
|
|
23049
|
-
if (tw * th >
|
|
23050
|
-
|
|
23049
|
+
if (tw * th > width2 * height) {
|
|
23050
|
+
width2 = tw;
|
|
23051
23051
|
height = th;
|
|
23052
23052
|
}
|
|
23053
23053
|
}
|
|
@@ -23071,8 +23071,8 @@ function mp4Info(b) {
|
|
|
23071
23071
|
};
|
|
23072
23072
|
walk(0, b.length, 0);
|
|
23073
23073
|
const dur = duration3 ?? mediaDuration;
|
|
23074
|
-
const fps =
|
|
23075
|
-
return { width, height, duration: dur, fps };
|
|
23074
|
+
const fps = width2 && height && dur && frames ? Math.round(frames / dur * 100) / 100 : void 0;
|
|
23075
|
+
return { width: width2, height, duration: dur, fps };
|
|
23076
23076
|
}
|
|
23077
23077
|
function probeMp4(b) {
|
|
23078
23078
|
const info = mp4Info(b);
|
|
@@ -23100,9 +23100,9 @@ async function probeMedia(ref) {
|
|
|
23100
23100
|
}
|
|
23101
23101
|
if (!head.length) throw new ProbeError("Arquivo vazio.");
|
|
23102
23102
|
if (kind === "image") {
|
|
23103
|
-
const
|
|
23104
|
-
if (!
|
|
23105
|
-
return { kind, width:
|
|
23103
|
+
const dim2 = probePng(head) ?? probeJpeg(head) ?? probeWebp(head);
|
|
23104
|
+
if (!dim2) throw new ProbeError("N\xE3o consegui ler as dimens\xF5es da imagem pelo cabe\xE7alho.");
|
|
23105
|
+
return { kind, width: dim2.width, height: dim2.height };
|
|
23106
23106
|
}
|
|
23107
23107
|
let info = probeMp4(head);
|
|
23108
23108
|
if (!info) {
|
|
@@ -23186,8 +23186,8 @@ function probeMp3(b) {
|
|
|
23186
23186
|
function probeBuffer(b, mime) {
|
|
23187
23187
|
const round = (n) => Math.round(n * 1e3) / 1e3;
|
|
23188
23188
|
if (mime.startsWith("image/")) {
|
|
23189
|
-
const
|
|
23190
|
-
return
|
|
23189
|
+
const dim2 = probePng(b) ?? probeJpeg(b) ?? probeWebp(b) ?? probeGif(b);
|
|
23190
|
+
return dim2 ? { width: dim2.width, height: dim2.height } : null;
|
|
23191
23191
|
}
|
|
23192
23192
|
if (/^(video|audio)\/(mp4|quicktime|x-m4v|x-m4a|aac)$/.test(mime) || probeMp4Head(b)) {
|
|
23193
23193
|
const info = mp4Info(b);
|
|
@@ -23316,14 +23316,14 @@ function registerUpscaleTools(server, client, cfg) {
|
|
|
23316
23316
|
`"${variant}" n\xE3o \xE9 um modelo de ${isVideo ? "v\xEDdeo" : "imagem"}. Use um destes: ${allowed.join(", ")}.`
|
|
23317
23317
|
);
|
|
23318
23318
|
}
|
|
23319
|
-
let
|
|
23319
|
+
let width2 = args.source_width;
|
|
23320
23320
|
let height = args.source_height;
|
|
23321
23321
|
let duration3 = args.source_duration;
|
|
23322
23322
|
let fps = args.source_fps;
|
|
23323
|
-
if (!
|
|
23323
|
+
if (!width2 || !height || isVideo && !duration3) {
|
|
23324
23324
|
try {
|
|
23325
23325
|
const probed = await probeMedia(args.file);
|
|
23326
|
-
|
|
23326
|
+
width2 = width2 ?? probed.width;
|
|
23327
23327
|
height = height ?? probed.height;
|
|
23328
23328
|
duration3 = duration3 ?? probed.duration;
|
|
23329
23329
|
fps = fps ?? probed.fps;
|
|
@@ -23358,7 +23358,7 @@ function registerUpscaleTools(server, client, cfg) {
|
|
|
23358
23358
|
model: variant,
|
|
23359
23359
|
upscale_factor: factor,
|
|
23360
23360
|
prompt: args.prompt || label,
|
|
23361
|
-
source_width:
|
|
23361
|
+
source_width: width2,
|
|
23362
23362
|
source_height: height,
|
|
23363
23363
|
source_duration: duration3,
|
|
23364
23364
|
source_fps: fps,
|
|
@@ -23414,7 +23414,7 @@ ${footer}`);
|
|
|
23414
23414
|
}
|
|
23415
23415
|
const urls = outputUrls(status);
|
|
23416
23416
|
const list = urls.map((u, i) => `${i + 1}. ${u}`).join("\n") || "(sem URLs)";
|
|
23417
|
-
const saida =
|
|
23417
|
+
const saida = width2 && height ? ` \u2014 sa\xEDda ${width2 * factor} \xD7 ${height * factor}` : "";
|
|
23418
23418
|
const semAudio = isVideo ? "\nO \xE1udio n\xE3o foi mantido (o Topaz descarta a faixa)." : "";
|
|
23419
23419
|
return textResult(
|
|
23420
23420
|
`\u2705 ${isVideo ? "V\xEDdeo" : "Imagem"} melhorado em ${factor}x com ${variant}${saida}.
|
|
@@ -23688,14 +23688,14 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
|
|
|
23688
23688
|
},
|
|
23689
23689
|
async (args) => {
|
|
23690
23690
|
try {
|
|
23691
|
-
let
|
|
23691
|
+
let width2 = args.source_width;
|
|
23692
23692
|
let height = args.source_height;
|
|
23693
23693
|
let duration3 = args.source_duration;
|
|
23694
23694
|
let fps = args.source_fps;
|
|
23695
|
-
if (args.file && (!
|
|
23695
|
+
if (args.file && (!width2 || !height)) {
|
|
23696
23696
|
try {
|
|
23697
23697
|
const probed = await probeMedia(args.file);
|
|
23698
|
-
|
|
23698
|
+
width2 = width2 ?? probed.width;
|
|
23699
23699
|
height = height ?? probed.height;
|
|
23700
23700
|
duration3 = duration3 ?? probed.duration;
|
|
23701
23701
|
fps = fps ?? probed.fps;
|
|
@@ -23714,7 +23714,7 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
|
|
|
23714
23714
|
quality: args.quality,
|
|
23715
23715
|
upscale_factor: args.upscale_factor,
|
|
23716
23716
|
model_variant: args.model_variant,
|
|
23717
|
-
source_width:
|
|
23717
|
+
source_width: width2,
|
|
23718
23718
|
source_height: height,
|
|
23719
23719
|
source_duration: duration3,
|
|
23720
23720
|
source_fps: fps
|
|
@@ -24397,11 +24397,14 @@ function isValidNode(key, v) {
|
|
|
24397
24397
|
function isValidEdge(key, v) {
|
|
24398
24398
|
return isRecord(v) && hasOnlyKeys(v, EDGE_KEYS) && v.id === key && typeof v.source === "string" && typeof v.sourcePort === "string" && typeof v.target === "string" && typeof v.targetPort === "string" && isJsonBag(v.data) && isStringOrNull(v.createdBy) && isFiniteNumber(v.createdAt);
|
|
24399
24399
|
}
|
|
24400
|
+
var MEASURED_KEY = "__measured";
|
|
24400
24401
|
function checkNodePath(path) {
|
|
24401
24402
|
const segments = path.split(".");
|
|
24402
24403
|
if (segments.some((s) => s.length === 0)) return `caminho inv\xE1lido "${path}"`;
|
|
24403
24404
|
if (segments.some((s) => FORBIDDEN_SEGMENTS.includes(s))) return `segmento proibido em "${path}"`;
|
|
24404
24405
|
const [root, ...rest] = segments;
|
|
24406
|
+
if (root === "data" && rest[0] === MEASURED_KEY)
|
|
24407
|
+
return `data.${MEASURED_KEY} \xE9 o tamanho medido pela tela do Canvas: s\xF3 o Canvas grava`;
|
|
24405
24408
|
if (root === "data" && rest.length >= 1) return null;
|
|
24406
24409
|
if (root === "position" && rest.length === 1 && ["x", "y"].includes(rest[0]) || root === "size" && rest.length === 1 && ["w", "h"].includes(rest[0])) return null;
|
|
24407
24410
|
return `s\xF3 d\xE1 para editar data.*, position.x/y e size.w/h (recebido "${path}")`;
|
|
@@ -24549,6 +24552,120 @@ function packEnvelopes(boardId3, ops, actorId, limit = MAX_OP_BYTES - 1024) {
|
|
|
24549
24552
|
return out;
|
|
24550
24553
|
}
|
|
24551
24554
|
|
|
24555
|
+
// src/canvas/layout.ts
|
|
24556
|
+
function readMeasured(node) {
|
|
24557
|
+
const m = node.data[MEASURED_KEY];
|
|
24558
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) return null;
|
|
24559
|
+
const { w, h, at } = m;
|
|
24560
|
+
if (typeof w !== "number" || typeof h !== "number" || !Number.isFinite(w) || !Number.isFinite(h) || w <= 0 || h <= 0) return null;
|
|
24561
|
+
return { w, h, at: typeof at === "string" ? at : null };
|
|
24562
|
+
}
|
|
24563
|
+
var SAME_WIDTH = 2;
|
|
24564
|
+
var WRAP_KINDS = /* @__PURE__ */ new Set(["prompt", "text", "block", "note", "label", "llm", "narration", "copy", "review", "shotlist"]);
|
|
24565
|
+
var SCALE_KINDS = /* @__PURE__ */ new Set(["image", "video", "media"]);
|
|
24566
|
+
var kindOf = (node) => typeof node.data[KIND_FIELD] === "string" ? node.data[KIND_FIELD] : "";
|
|
24567
|
+
function heightAt(kind, baseW, baseH, newW) {
|
|
24568
|
+
if (Math.abs(newW - baseW) <= SAME_WIDTH) return baseH;
|
|
24569
|
+
if (SCALE_KINDS.has(kind) && newW > baseW) return Math.ceil(baseH * newW / baseW);
|
|
24570
|
+
if (WRAP_KINDS.has(kind) && newW < baseW) return Math.ceil(baseH * baseW / newW);
|
|
24571
|
+
return baseH;
|
|
24572
|
+
}
|
|
24573
|
+
function usedSize(node, floorW = node.size.w) {
|
|
24574
|
+
const kind = kindOf(node);
|
|
24575
|
+
const m = readMeasured(node);
|
|
24576
|
+
if (!m) {
|
|
24577
|
+
const h = Math.max(node.size.h, heightAt(kind, node.size.w, node.size.h, floorW));
|
|
24578
|
+
return { w: floorW, h, source: "gravado", at: null, estimated: h !== node.size.h };
|
|
24579
|
+
}
|
|
24580
|
+
const keepsWidth = Math.abs(floorW - node.size.w) <= SAME_WIDTH;
|
|
24581
|
+
return {
|
|
24582
|
+
w: keepsWidth ? Math.max(floorW, m.w) : floorW,
|
|
24583
|
+
h: Math.max(node.size.h, heightAt(kind, m.w, m.h, floorW)),
|
|
24584
|
+
source: "medido",
|
|
24585
|
+
at: m.at,
|
|
24586
|
+
estimated: Math.abs(floorW - m.w) > SAME_WIDTH
|
|
24587
|
+
};
|
|
24588
|
+
}
|
|
24589
|
+
function sizeLabel(node) {
|
|
24590
|
+
const u = usedSize(node);
|
|
24591
|
+
const base = `${Math.round(u.w)}\xD7${Math.round(u.h)} ${u.source}`;
|
|
24592
|
+
if (u.source === "medido" && u.estimated) return `${base} (medido em ${Math.round(readMeasured(node).w)} de largura: altura estimada)`;
|
|
24593
|
+
return base;
|
|
24594
|
+
}
|
|
24595
|
+
var DEFAULT_WIDTHS = { prompt: 1024, image: 560, video: 560, reference: 320, frame: 320, title: 1024 };
|
|
24596
|
+
var DEFAULT_GAP = 80;
|
|
24597
|
+
var FRAME_PORTS = /* @__PURE__ */ new Set(["frame"]);
|
|
24598
|
+
var FEEDS = /* @__PURE__ */ new Set(["prompt", "image", "video"]);
|
|
24599
|
+
var outOf = (doc, id) => Object.values(doc.edges).filter((e) => e.source === id);
|
|
24600
|
+
function roleOf(doc, node, asTitle) {
|
|
24601
|
+
if (asTitle) return "title";
|
|
24602
|
+
const kind = kindOf(node);
|
|
24603
|
+
if (kind === "prompt" || kind === "video") return kind;
|
|
24604
|
+
if (kind !== "media" && kind !== "image") return "other";
|
|
24605
|
+
const feedsFrame = outOf(doc, node.id).some((e) => FRAME_PORTS.has(e.targetPort) && doc.nodes[e.target] && kindOf(doc.nodes[e.target]) === "video");
|
|
24606
|
+
if (feedsFrame) return "frame";
|
|
24607
|
+
return kind === "media" ? "reference" : "image";
|
|
24608
|
+
}
|
|
24609
|
+
function hangsRight(doc, node) {
|
|
24610
|
+
const kind = kindOf(node);
|
|
24611
|
+
if (kind !== "media" && kind !== "image") return false;
|
|
24612
|
+
const out = outOf(doc, node.id);
|
|
24613
|
+
if (!out.length) return kind === "media";
|
|
24614
|
+
return out.some((e) => doc.nodes[e.target] && FEEDS.has(kindOf(doc.nodes[e.target])));
|
|
24615
|
+
}
|
|
24616
|
+
var overlap = (a, b) => a.x < b.x + b.w && b.x < a.x + a.w && a.y < b.y + b.h && b.y < a.y + a.h;
|
|
24617
|
+
function arrangeRows(doc, rows, origin, gap = DEFAULT_GAP, widths = DEFAULT_WIDTHS) {
|
|
24618
|
+
const placed = [];
|
|
24619
|
+
const floorOf = (node, role) => role === "other" ? node.size.w : widths[role];
|
|
24620
|
+
const place = (id, row, asTitle, x, y2, alignRight) => {
|
|
24621
|
+
const node = doc.nodes[id];
|
|
24622
|
+
const role = roleOf(doc, node, asTitle);
|
|
24623
|
+
const floorW = floorOf(node, role);
|
|
24624
|
+
const u = usedSize(node, floorW);
|
|
24625
|
+
const w = Math.ceil(u.w);
|
|
24626
|
+
const h = Math.ceil(u.h);
|
|
24627
|
+
const px = alignRight !== void 0 ? alignRight - w : x;
|
|
24628
|
+
const p = { id, row, role, x: Math.round(px), y: Math.round(y2), w, h, source: u.source, estimated: u.estimated, floorW };
|
|
24629
|
+
placed.push(p);
|
|
24630
|
+
return p;
|
|
24631
|
+
};
|
|
24632
|
+
let y = origin.y;
|
|
24633
|
+
rows.forEach((row, r) => {
|
|
24634
|
+
if (row.title) {
|
|
24635
|
+
const t = place(row.title, r, true, origin.x, y);
|
|
24636
|
+
y = t.y + t.h + Math.round(gap / 2);
|
|
24637
|
+
}
|
|
24638
|
+
const top = y;
|
|
24639
|
+
const leftW = Math.max(0, ...row.left.map((id) => Math.ceil(usedSize(doc.nodes[id], floorOf(doc.nodes[id], roleOf(doc, doc.nodes[id], false))).w)));
|
|
24640
|
+
let leftEnd = top;
|
|
24641
|
+
row.left.forEach((id, i) => {
|
|
24642
|
+
const right2 = hangsRight(doc, doc.nodes[id]);
|
|
24643
|
+
const p = place(id, r, false, origin.x, i === 0 ? top : leftEnd + gap, right2 ? origin.x + leftW : void 0);
|
|
24644
|
+
leftEnd = p.y + p.h;
|
|
24645
|
+
});
|
|
24646
|
+
const rightX = origin.x + (row.left.length ? leftW + gap : 0);
|
|
24647
|
+
let rightEnd = top;
|
|
24648
|
+
row.right.forEach((id, i) => {
|
|
24649
|
+
const p = place(id, r, false, rightX, i === 0 ? top : rightEnd + gap);
|
|
24650
|
+
rightEnd = p.y + p.h;
|
|
24651
|
+
});
|
|
24652
|
+
y = Math.max(leftEnd, rightEnd, top) + gap * 2;
|
|
24653
|
+
});
|
|
24654
|
+
const inner = [];
|
|
24655
|
+
for (let i = 0; i < placed.length; i++) {
|
|
24656
|
+
for (let j = i + 1; j < placed.length; j++) if (overlap(placed[i], placed[j])) inner.push([placed[i].id, placed[j].id]);
|
|
24657
|
+
}
|
|
24658
|
+
const ids = new Set(placed.map((p) => p.id));
|
|
24659
|
+
const others = Object.values(doc.nodes).filter((n) => !ids.has(n.id) && kindOf(n) !== "section").map((n) => ({ id: n.id, x: n.position.x, y: n.position.y, ...usedSize(n) }));
|
|
24660
|
+
const outer = [];
|
|
24661
|
+
for (const p of placed) for (const o of others) if (overlap(p, o)) outer.push([p.id, o.id]);
|
|
24662
|
+
const left = Math.min(origin.x, ...placed.map((p) => p.x));
|
|
24663
|
+
const right = Math.max(origin.x, ...placed.map((p) => p.x + p.w));
|
|
24664
|
+
const below = others.filter((o) => o.x < right && left < o.x + o.w).map((o) => o.y + o.h);
|
|
24665
|
+
const freeOrigin = { x: origin.x, y: below.length ? Math.max(origin.y, Math.ceil(Math.max(...below) + gap * 2)) : origin.y };
|
|
24666
|
+
return { placed, inner, outer, freeOrigin };
|
|
24667
|
+
}
|
|
24668
|
+
|
|
24552
24669
|
// src/canvas/summary.ts
|
|
24553
24670
|
var MAX_TEXT = 160;
|
|
24554
24671
|
var MAX_BLOCKS = 40;
|
|
@@ -24571,8 +24688,14 @@ var HEAVY_KEYS = /* @__PURE__ */ new Set([
|
|
|
24571
24688
|
"settings",
|
|
24572
24689
|
"enabledKeys",
|
|
24573
24690
|
"checks",
|
|
24574
|
-
"versions"
|
|
24691
|
+
"versions",
|
|
24692
|
+
MEASURED_KEY
|
|
24575
24693
|
]);
|
|
24694
|
+
function visibleData(data) {
|
|
24695
|
+
if (!Object.hasOwn(data, MEASURED_KEY)) return data;
|
|
24696
|
+
const { [MEASURED_KEY]: _measured, ...rest } = data;
|
|
24697
|
+
return rest;
|
|
24698
|
+
}
|
|
24576
24699
|
var NAME_KEYS = /* @__PURE__ */ new Set([KIND_FIELD, "label", "nodeTitle"]);
|
|
24577
24700
|
function oneLine(text, max = MAX_TEXT) {
|
|
24578
24701
|
const flat = text.replace(/\s+/g, " ").trim();
|
|
@@ -24625,7 +24748,7 @@ function rowsLines(rows) {
|
|
|
24625
24748
|
function nodeLines(node, run) {
|
|
24626
24749
|
const kind = nodeKind(node);
|
|
24627
24750
|
const spec = kind ? KINDS[kind] : void 0;
|
|
24628
|
-
const head = `- ${node.id} \xB7 ${kind ?? node.type} \xB7 "${oneLine(nodeName(node), 60)}" @(${Math.round(node.position.x)},${Math.round(node.position.y)})`;
|
|
24751
|
+
const head = `- ${node.id} \xB7 ${kind ?? node.type} \xB7 "${oneLine(nodeName(node), 60)}" @(${Math.round(node.position.x)},${Math.round(node.position.y)}) ${sizeLabel(node)}`;
|
|
24629
24752
|
const facts = [];
|
|
24630
24753
|
const extra = [];
|
|
24631
24754
|
const d = node.data;
|
|
@@ -24690,7 +24813,7 @@ function boardSummary(snap) {
|
|
|
24690
24813
|
lines.push(`IA Lab: vinculado${label ? ` a ${String(label)}` : ""}.`);
|
|
24691
24814
|
}
|
|
24692
24815
|
if (doc.meta?.guide) lines.push(`Guia: ${oneLine(doc.meta.guide)}`);
|
|
24693
|
-
lines.push("", "N\xF3s:");
|
|
24816
|
+
lines.push("", "N\xF3s (tamanho: medido = como est\xE1 na tela; gravado = piso, o cart\xE3o pode ser maior):");
|
|
24694
24817
|
if (!nodes.length) lines.push("(vazio)");
|
|
24695
24818
|
for (const n of nodes) lines.push(...nodeLines(n, runs.get(n.id)));
|
|
24696
24819
|
lines.push("", "Liga\xE7\xF5es:");
|
|
@@ -24734,12 +24857,28 @@ function nodeDetail(node) {
|
|
|
24734
24857
|
}
|
|
24735
24858
|
return v;
|
|
24736
24859
|
};
|
|
24737
|
-
const json2 = JSON.stringify(strip(node.data, 0, ""));
|
|
24860
|
+
const json2 = JSON.stringify(strip(visibleData(node.data), 0, ""));
|
|
24738
24861
|
const note = cuts ? `
|
|
24739
24862
|
(${cuts} corte(s) marcado(s) com path=\u2026: leia inteiro com canvas_read_node e esse path)` : "";
|
|
24740
|
-
return `${node.id} \xB7 ${nodeKind(node) ?? node.type} \xB7 "${nodeName(node)}"
|
|
24863
|
+
return `${node.id} \xB7 ${nodeKind(node) ?? node.type} \xB7 "${nodeName(node)}" \xB7 ${sizeLabel(node)}
|
|
24741
24864
|
${json2}${note}`;
|
|
24742
24865
|
}
|
|
24866
|
+
var isMeasureNoise = (op) => op.type === "node.set" && typeof op.path === "string" && (op.path === `data.${MEASURED_KEY}` || op.path.startsWith(`data.${MEASURED_KEY}.`));
|
|
24867
|
+
function withoutMeasureNoise(envelopes) {
|
|
24868
|
+
const clean = (op) => {
|
|
24869
|
+
if (op.type === "batch" && Array.isArray(op.ops)) {
|
|
24870
|
+
const ops = op.ops.map((o) => clean(o)).filter((o) => o !== null);
|
|
24871
|
+
return ops.length ? { ...op, ops } : null;
|
|
24872
|
+
}
|
|
24873
|
+
return isMeasureNoise(op) ? null : op;
|
|
24874
|
+
};
|
|
24875
|
+
const out = [];
|
|
24876
|
+
for (const e of envelopes) {
|
|
24877
|
+
const op = clean(e.op);
|
|
24878
|
+
if (op) out.push({ ...e, op });
|
|
24879
|
+
}
|
|
24880
|
+
return out;
|
|
24881
|
+
}
|
|
24743
24882
|
function changesSince(envelopes, doc) {
|
|
24744
24883
|
const count = /* @__PURE__ */ new Map();
|
|
24745
24884
|
for (const e of envelopes) {
|
|
@@ -25049,6 +25188,158 @@ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_
|
|
|
25049
25188
|
);
|
|
25050
25189
|
}
|
|
25051
25190
|
|
|
25191
|
+
// src/tools/canvas-layout.ts
|
|
25192
|
+
var nodeIdArg = external_exports.string().min(1).max(128);
|
|
25193
|
+
var width = external_exports.number().finite().min(40).max(2e4);
|
|
25194
|
+
var ROLE_TEXT = {
|
|
25195
|
+
title: "t\xEDtulo",
|
|
25196
|
+
prompt: "prompt",
|
|
25197
|
+
image: "imagem",
|
|
25198
|
+
video: "v\xEDdeo",
|
|
25199
|
+
reference: "ref",
|
|
25200
|
+
frame: "frame",
|
|
25201
|
+
other: "outro"
|
|
25202
|
+
};
|
|
25203
|
+
function checkRows(doc, rows) {
|
|
25204
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25205
|
+
for (const [i, row] of rows.entries()) {
|
|
25206
|
+
const ids = [...row.title ? [row.title] : [], ...row.left, ...row.right];
|
|
25207
|
+
if (!ids.length) throw new CanvasToolError(`rows[${i}] est\xE1 vazia: informe title, left ou right.`);
|
|
25208
|
+
for (const id of ids) {
|
|
25209
|
+
const node = doc.nodes[id];
|
|
25210
|
+
if (!node) throw new CanvasToolError(`rows[${i}]: o n\xF3 ${id} n\xE3o existe no board. Releia com canvas_read_board.`);
|
|
25211
|
+
if (node.data[KIND_FIELD] === "section") throw new CanvasToolError(`rows[${i}]: ${id} \xE9 uma Se\xE7\xE3o (fundo), n\xE3o entra no arranjo.`);
|
|
25212
|
+
if (seen.has(id)) throw new CanvasToolError(`O n\xF3 ${id} aparece duas vezes nas linhas: cada n\xF3 entra uma vez s\xF3.`);
|
|
25213
|
+
seen.add(id);
|
|
25214
|
+
}
|
|
25215
|
+
}
|
|
25216
|
+
}
|
|
25217
|
+
function layoutOps(doc, a) {
|
|
25218
|
+
const ops = [];
|
|
25219
|
+
for (const p of a.placed) {
|
|
25220
|
+
const node = doc.nodes[p.id];
|
|
25221
|
+
const set = (path, now, next) => {
|
|
25222
|
+
if (now !== next) ops.push({ type: "node.set", nodeId: p.id, path, value: setValue(next) });
|
|
25223
|
+
};
|
|
25224
|
+
set("position.x", node.position.x, p.x);
|
|
25225
|
+
set("position.y", node.position.y, p.y);
|
|
25226
|
+
set("size.w", node.size.w, p.floorW);
|
|
25227
|
+
}
|
|
25228
|
+
return ops;
|
|
25229
|
+
}
|
|
25230
|
+
function report(doc, a) {
|
|
25231
|
+
const name = (id) => `"${oneLine(nodeName(doc.nodes[id]), 36)}"`;
|
|
25232
|
+
const lines = [];
|
|
25233
|
+
const unmeasured = a.placed.filter((p) => p.source === "gravado");
|
|
25234
|
+
const estimated = a.placed.filter((p) => p.source === "medido" && p.estimated);
|
|
25235
|
+
if (unmeasured.length || estimated.length) {
|
|
25236
|
+
lines.push("A conferir (a altura usada pode n\xE3o ser a da tela):");
|
|
25237
|
+
if (unmeasured.length) {
|
|
25238
|
+
lines.push(
|
|
25239
|
+
`- sem medida da tela (usei o gravado): o cart\xE3o est\xE1 no piso, e a\xED o gravado \xE9 o da tela, ou o board n\xE3o abriu desde a \xFAltima mudan\xE7a, e a\xED pode ser maior: ${unmeasured.map((p) => name(p.id)).join(", ")}`
|
|
25240
|
+
);
|
|
25241
|
+
}
|
|
25242
|
+
if (estimated.length) {
|
|
25243
|
+
const how = estimated.map((p) => `${name(p.id)} (medido com ${Math.round(readMeasured(doc.nodes[p.id]).w)} de largura)`);
|
|
25244
|
+
lines.push(`- altura estimada com folga porque a largura mudou: ${how.join(", ")}`);
|
|
25245
|
+
}
|
|
25246
|
+
lines.push(
|
|
25247
|
+
"Se o board n\xE3o abriu desde a \xFAltima mudan\xE7a, abra (o Canvas mede o cart\xE3o que passa do piso) e rode o mesmo canvas_arrange_rows de novo para fechar sobra ou encosto."
|
|
25248
|
+
);
|
|
25249
|
+
} else {
|
|
25250
|
+
lines.push("Todos os n\xF3s arrumados t\xEAm medida da tela.");
|
|
25251
|
+
}
|
|
25252
|
+
const pairs = (list) => list.map(([x, y]) => `${name(x)} \xD7 ${name(y)}`).join("; ");
|
|
25253
|
+
lines.push(
|
|
25254
|
+
a.inner.length || a.outer.length ? `Sobreposi\xE7\xF5es com os tamanhos usados: ${pairs([...a.inner, ...a.outer])}.` : "Sobreposi\xE7\xF5es com os tamanhos usados: nenhuma."
|
|
25255
|
+
);
|
|
25256
|
+
lines.push('Caixas ([papel] id "nome" @(x,y) largura\xD7altura usada, origem da altura):');
|
|
25257
|
+
const rows = /* @__PURE__ */ new Map();
|
|
25258
|
+
for (const p of a.placed) {
|
|
25259
|
+
const item = `[${ROLE_TEXT[p.role]}] ${p.id} ${name(p.id)} @(${p.x},${p.y}) ${p.w}\xD7${p.h} ${p.source}${p.estimated ? " estimado" : ""}`;
|
|
25260
|
+
rows.set(p.row, [...rows.get(p.row) ?? [], item]);
|
|
25261
|
+
}
|
|
25262
|
+
for (const [r, items] of rows) lines.push(`- linha ${r + 1}: ${items.join(" \xB7 ")}`);
|
|
25263
|
+
const resized = a.placed.filter((p) => p.floorW !== doc.nodes[p.id].size.w);
|
|
25264
|
+
if (resized.length) lines.push(`Largura (size.w): ${resized.map((p) => `${name(p.id)} ${doc.nodes[p.id].size.w}\u2192${p.floorW}`).join(", ")}.`);
|
|
25265
|
+
const titles = a.placed.filter((p) => p.role === "title" && doc.nodes[p.id].data[KIND_FIELD] === "label" && doc.nodes[p.id].data.size !== "titulo");
|
|
25266
|
+
if (titles.length) {
|
|
25267
|
+
lines.push(
|
|
25268
|
+
`T\xEDtulo sem o texto de t\xEDtulo: ${titles.map((p) => name(p.id)).join(", ")}. Troque com canvas_update_node size:"titulo" (o arranjo s\xF3 grava posi\xE7\xE3o e tamanho).`
|
|
25269
|
+
);
|
|
25270
|
+
}
|
|
25271
|
+
return lines.join("\n");
|
|
25272
|
+
}
|
|
25273
|
+
function registerCanvasLayoutTools(server, deps) {
|
|
25274
|
+
const { api, run, edit } = deps;
|
|
25275
|
+
server.registerTool(
|
|
25276
|
+
"canvas_arrange_rows",
|
|
25277
|
+
{
|
|
25278
|
+
title: "Canvas: arrumar em linhas",
|
|
25279
|
+
description: "Arruma n\xF3s em linhas, uma por vers\xE3o (revis\xE3o de imagem) ou por take (anima\xE7\xE3o), de cima para baixo a partir de origin: title em cima; \xE0 esquerda a coluna left (prompt; m\xEDdia/imagem que alimenta o prompt ou o gerador, como a refer\xEAncia e o frame de entrada, fica alinhada \xE0 direita embaixo dele); \xE0 direita a coluna right (gerador) com o topo no do prompt. Qualquer tipo entra em qualquer coluna. Usa o tamanho medido da tela (ou o gravado, avisando) e p\xF5e a largura pelo tipo real do n\xF3: prompt 1024, gerador de imagem 560, de v\xEDdeo 560, refer\xEAncia (m\xEDdia) 320, frame (m\xEDdia/imagem ligada na porta frame do v\xEDdeo) 320, t\xEDtulo 1024. Grava s\xF3 posi\xE7\xE3o e tamanho num passo, sem pedir releitura. Devolve as caixas, as sobreposi\xE7\xF5es com os tamanhos usados e os n\xF3s sem medida (a altura deles pode crescer quando o board abrir: rode de novo depois). Se a \xE1rea encostar em outro n\xF3 do board, n\xE3o grava nada e sugere uma origem livre. dry_run: true s\xF3 calcula.",
|
|
25280
|
+
inputSchema: {
|
|
25281
|
+
board_id: external_exports.string().uuid(),
|
|
25282
|
+
origin: external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() }).describe("Canto de cima \xE0 esquerda da \xE1rea."),
|
|
25283
|
+
rows: external_exports.array(
|
|
25284
|
+
external_exports.object({
|
|
25285
|
+
title: nodeIdArg.optional().describe("N\xF3 do t\xEDtulo da linha (Texto livre)."),
|
|
25286
|
+
left: external_exports.array(nodeIdArg).max(20).optional().describe("Coluna da esquerda, de cima para baixo: prompt, depois a refer\xEAncia."),
|
|
25287
|
+
right: external_exports.array(nodeIdArg).max(20).optional().describe("Coluna da direita: gerador(es).")
|
|
25288
|
+
}).strict()
|
|
25289
|
+
).min(1).max(50),
|
|
25290
|
+
gap: external_exports.number().finite().min(0).max(1e3).optional().describe(`Espa\xE7o entre cart\xF5es (padr\xE3o ${DEFAULT_GAP}); o dobro entre linhas.`),
|
|
25291
|
+
widths: external_exports.object({
|
|
25292
|
+
prompt: width.optional(),
|
|
25293
|
+
image: width.optional(),
|
|
25294
|
+
video: width.optional(),
|
|
25295
|
+
generator: width.optional().describe("Atalho: image e video juntos."),
|
|
25296
|
+
reference: width.optional(),
|
|
25297
|
+
frame: width.optional(),
|
|
25298
|
+
title: width.optional()
|
|
25299
|
+
}).strict().optional().describe("Troca larguras da conven\xE7\xE3o (prompt 1024, image 560, video 560, reference 320, frame 320, title 1024)."),
|
|
25300
|
+
dry_run: external_exports.boolean().optional().describe("true: s\xF3 calcula e mostra, sem gravar.")
|
|
25301
|
+
},
|
|
25302
|
+
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
25303
|
+
},
|
|
25304
|
+
async (args) => run(async () => {
|
|
25305
|
+
const rows = args.rows.map((r) => ({ title: r.title, left: r.left ?? [], right: r.right ?? [] }));
|
|
25306
|
+
const { generator, ...own } = args.widths ?? {};
|
|
25307
|
+
const widths = { ...DEFAULT_WIDTHS, ...generator ? { image: generator, video: generator } : {}, ...own };
|
|
25308
|
+
const gap = args.gap ?? DEFAULT_GAP;
|
|
25309
|
+
const plan = (doc) => {
|
|
25310
|
+
checkRows(doc, rows);
|
|
25311
|
+
const a = arrangeRows(doc, rows, args.origin, gap, widths);
|
|
25312
|
+
return { a, ops: layoutOps(doc, a), text: report(doc, a) };
|
|
25313
|
+
};
|
|
25314
|
+
const count = (ops) => new Set(ops.map((o) => o.type === "node.set" ? o.nodeId : "")).size;
|
|
25315
|
+
const blocked = (a) => `A \xE1rea encosta em n\xF3s que n\xE3o est\xE3o no arranjo. Nada foi gravado. Origem livre sugerida: {"x":${a.freeOrigin.x},"y":${a.freeOrigin.y}} (abaixo de tudo nessa faixa), ou tire esses n\xF3s do caminho.`;
|
|
25316
|
+
if (args.dry_run) {
|
|
25317
|
+
const { a, ops, text: text2 } = plan((await api.snapshot(args.board_id)).doc);
|
|
25318
|
+
const head = `Pr\xE9via, nada gravado: ${ops.length ? `${count(ops)} n\xF3(s) mudariam de lugar ou largura` : "tudo j\xE1 est\xE1 assim"}.`;
|
|
25319
|
+
return [head, ...a.outer.length ? [blocked(a)] : [], text2].join("\n");
|
|
25320
|
+
}
|
|
25321
|
+
let text = "";
|
|
25322
|
+
const done = await edit(
|
|
25323
|
+
args.board_id,
|
|
25324
|
+
async (doc) => {
|
|
25325
|
+
const p = plan(doc);
|
|
25326
|
+
text = p.text;
|
|
25327
|
+
if (p.a.outer.length) throw new CanvasToolError(`${blocked(p.a)}
|
|
25328
|
+
${p.text}`);
|
|
25329
|
+
return {
|
|
25330
|
+
ops: p.ops,
|
|
25331
|
+
targets: [],
|
|
25332
|
+
done: `Arranjo: ${rows.length} linha(s), ${p.a.placed.length} n\xF3(s), ${count(p.ops)} mudaram de lugar ou largura`
|
|
25333
|
+
};
|
|
25334
|
+
},
|
|
25335
|
+
{ split: true, layoutOnly: true }
|
|
25336
|
+
);
|
|
25337
|
+
return `${done}
|
|
25338
|
+
${text}`;
|
|
25339
|
+
})
|
|
25340
|
+
);
|
|
25341
|
+
}
|
|
25342
|
+
|
|
25052
25343
|
// src/canvas/upload.ts
|
|
25053
25344
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
25054
25345
|
import { basename as basename2, extname as extname3 } from "path";
|
|
@@ -25257,6 +25548,137 @@ ${payload(u)}`
|
|
|
25257
25548
|
);
|
|
25258
25549
|
}
|
|
25259
25550
|
|
|
25551
|
+
// src/canvas/mentions.ts
|
|
25552
|
+
var VIA_MENTION = "viaMention";
|
|
25553
|
+
var PROMPT_REFS_INPUT = "refs";
|
|
25554
|
+
var PROMPT_MENTION_REFS_INPUT = "mentionRefs";
|
|
25555
|
+
var MENTION_KEYS = ["template", "fields", "mentions"];
|
|
25556
|
+
var VARIABLE_SOURCE = String.raw`\{\{[ \t]*([\p{L}\p{N}_-]+)[ \t]*\}\}`;
|
|
25557
|
+
function extractVariables(text) {
|
|
25558
|
+
const names = /* @__PURE__ */ new Set();
|
|
25559
|
+
for (const m of text.matchAll(new RegExp(VARIABLE_SOURCE, "gu"))) if (m[1]) names.add(m[1]);
|
|
25560
|
+
return [...names];
|
|
25561
|
+
}
|
|
25562
|
+
function allVariableNames(data) {
|
|
25563
|
+
const names = new Set(extractVariables(typeof data.template === "string" ? data.template : ""));
|
|
25564
|
+
const fields = data.fields;
|
|
25565
|
+
if (fields && typeof fields === "object" && !Array.isArray(fields)) {
|
|
25566
|
+
for (const [id, text] of Object.entries(fields)) {
|
|
25567
|
+
if (id !== "" && typeof text === "string" && text !== "") for (const n of extractVariables(text)) names.add(n);
|
|
25568
|
+
}
|
|
25569
|
+
}
|
|
25570
|
+
return [...names];
|
|
25571
|
+
}
|
|
25572
|
+
function normalizeMentions(value) {
|
|
25573
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
25574
|
+
const out = {};
|
|
25575
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
25576
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
25577
|
+
const { node, port } = entry;
|
|
25578
|
+
if (name.trim() !== "" && typeof node === "string" && node !== "" && typeof port === "string" && port !== "") {
|
|
25579
|
+
out[name] = { node, port };
|
|
25580
|
+
}
|
|
25581
|
+
}
|
|
25582
|
+
return out;
|
|
25583
|
+
}
|
|
25584
|
+
function mentionEdgeId(e) {
|
|
25585
|
+
return `mention:${e.source}:${e.sourcePort}>${e.target}:${e.targetPort}`;
|
|
25586
|
+
}
|
|
25587
|
+
var edgeKey = (e) => `${e.source}\0${e.sourcePort}\0${e.target}\0${e.targetPort}`;
|
|
25588
|
+
var isViaMention = (e) => e.data[VIA_MENTION] === true;
|
|
25589
|
+
function reachability(doc) {
|
|
25590
|
+
const adj = /* @__PURE__ */ new Map();
|
|
25591
|
+
const link = (s, t) => adj.set(s, (adj.get(s) ?? /* @__PURE__ */ new Set()).add(t));
|
|
25592
|
+
for (const e of Object.values(doc.edges)) link(e.source, e.target);
|
|
25593
|
+
const reaches = (from, to) => {
|
|
25594
|
+
const seen = /* @__PURE__ */ new Set();
|
|
25595
|
+
const stack = [from];
|
|
25596
|
+
while (stack.length) {
|
|
25597
|
+
for (const next of adj.get(stack.pop()) ?? []) {
|
|
25598
|
+
if (next === to) return true;
|
|
25599
|
+
if (!seen.has(next)) {
|
|
25600
|
+
seen.add(next);
|
|
25601
|
+
stack.push(next);
|
|
25602
|
+
}
|
|
25603
|
+
}
|
|
25604
|
+
}
|
|
25605
|
+
return false;
|
|
25606
|
+
};
|
|
25607
|
+
return { wouldCycle: (s, t) => s === t || reaches(t, s), accept: link };
|
|
25608
|
+
}
|
|
25609
|
+
function originHasPort(doc, origin) {
|
|
25610
|
+
const node = doc.nodes[origin.node];
|
|
25611
|
+
if (!node) return false;
|
|
25612
|
+
const kind = typeof node.data[KIND_FIELD] === "string" ? node.data[KIND_FIELD] : null;
|
|
25613
|
+
if (!kind || !KINDS[kind]) return false;
|
|
25614
|
+
return knownPort(kind, "out", origin.port) !== false;
|
|
25615
|
+
}
|
|
25616
|
+
function mentionEdgeOps(doc, promptIds, actorId, now = Date.now()) {
|
|
25617
|
+
const out = { ops: [], added: [], removed: [], skipped: [] };
|
|
25618
|
+
const reach = reachability(doc);
|
|
25619
|
+
const edges = Object.values(doc.edges);
|
|
25620
|
+
const desired = /* @__PURE__ */ new Map();
|
|
25621
|
+
const managed = [];
|
|
25622
|
+
for (const id of [...new Set(promptIds)]) {
|
|
25623
|
+
const prompt = doc.nodes[id];
|
|
25624
|
+
if (!prompt || prompt.data[KIND_FIELD] !== "prompt") continue;
|
|
25625
|
+
const into = edges.filter((e) => e.target === id);
|
|
25626
|
+
const vars = new Set(allVariableNames(prompt.data));
|
|
25627
|
+
const mentions2 = normalizeMentions(prompt.data.mentions);
|
|
25628
|
+
const mentionPorts = new Set(into.filter(isViaMention).map((e) => e.targetPort));
|
|
25629
|
+
managed.push(...into.filter((e) => isViaMention(e) && e.targetPort !== PROMPT_REFS_INPUT && e.targetPort !== PROMPT_MENTION_REFS_INPUT));
|
|
25630
|
+
for (const port of /* @__PURE__ */ new Set([...vars, ...mentionPorts])) {
|
|
25631
|
+
if (port === PROMPT_REFS_INPUT || port === PROMPT_MENTION_REFS_INPUT) continue;
|
|
25632
|
+
const origin = vars.has(port) ? mentions2[port] : void 0;
|
|
25633
|
+
if (!origin) continue;
|
|
25634
|
+
if (into.some((e) => e.targetPort === port && !isViaMention(e))) {
|
|
25635
|
+
out.skipped.push(`{{${port}}}: a porta j\xE1 recebe uma liga\xE7\xE3o feita \xE0 m\xE3o`);
|
|
25636
|
+
continue;
|
|
25637
|
+
}
|
|
25638
|
+
if (!doc.nodes[origin.node]) {
|
|
25639
|
+
out.skipped.push(`{{${port}}}: o n\xF3 de origem ${origin.node} n\xE3o existe`);
|
|
25640
|
+
continue;
|
|
25641
|
+
}
|
|
25642
|
+
if (!originHasPort(doc, origin)) {
|
|
25643
|
+
out.skipped.push(`{{${port}}}: a porta "${origin.port}" n\xE3o existe em ${origin.node}`);
|
|
25644
|
+
continue;
|
|
25645
|
+
}
|
|
25646
|
+
if (reach.wouldCycle(origin.node, id)) {
|
|
25647
|
+
out.skipped.push(`{{${port}}}: a liga\xE7\xE3o fecharia um ciclo`);
|
|
25648
|
+
continue;
|
|
25649
|
+
}
|
|
25650
|
+
const link = { source: origin.node, sourcePort: origin.port, target: id, targetPort: port };
|
|
25651
|
+
desired.set(edgeKey(link), link);
|
|
25652
|
+
reach.accept(origin.node, id);
|
|
25653
|
+
}
|
|
25654
|
+
}
|
|
25655
|
+
for (const e of managed) {
|
|
25656
|
+
if (desired.has(edgeKey(e))) continue;
|
|
25657
|
+
out.ops.push({ type: "edge.remove", edgeId: e.id });
|
|
25658
|
+
out.removed.push(e);
|
|
25659
|
+
}
|
|
25660
|
+
for (const [key, link] of desired) {
|
|
25661
|
+
if (edges.some((e) => edgeKey(e) === key)) continue;
|
|
25662
|
+
const edgeId = mentionEdgeId(link);
|
|
25663
|
+
out.ops.push({
|
|
25664
|
+
type: "edge.add",
|
|
25665
|
+
edgeId,
|
|
25666
|
+
edge: { id: edgeId, ...link, data: { [VIA_MENTION]: true }, createdBy: actorId, createdAt: now }
|
|
25667
|
+
});
|
|
25668
|
+
out.added.push(link);
|
|
25669
|
+
}
|
|
25670
|
+
return out;
|
|
25671
|
+
}
|
|
25672
|
+
function describeMentionSync(sync, name) {
|
|
25673
|
+
const parts = [];
|
|
25674
|
+
if (sync.added.length) {
|
|
25675
|
+
parts.push(`liga\xE7\xE3o de men\xE7\xE3o criada: ${sync.added.map((l) => `${name(l.source)}.${l.sourcePort} \u2192 {{${l.targetPort}}}`).join(", ")}`);
|
|
25676
|
+
}
|
|
25677
|
+
if (sync.removed.length) parts.push(`liga\xE7\xE3o de men\xE7\xE3o removida: ${sync.removed.map((e) => `{{${e.targetPort}}}`).join(", ")}`);
|
|
25678
|
+
if (sync.skipped.length) parts.push(`men\xE7\xE3o sem liga\xE7\xE3o: ${sync.skipped.join("; ")}`);
|
|
25679
|
+
return parts.length ? ` ${parts.join(". ")}.` : "";
|
|
25680
|
+
}
|
|
25681
|
+
|
|
25260
25682
|
// src/tools/canvas.ts
|
|
25261
25683
|
var REJECT_TEXT = {
|
|
25262
25684
|
"node-missing": "o n\xF3 n\xE3o existe mais (algu\xE9m apagou)",
|
|
@@ -25265,6 +25687,7 @@ var REJECT_TEXT = {
|
|
|
25265
25687
|
"too-large": "opera\xE7\xE3o grande demais (64 KB) ou com m\xEDdia embutida"
|
|
25266
25688
|
};
|
|
25267
25689
|
var READ = "Exige canvas_read_board nesta sess\xE3o (e de novo depois de uma recusa). ";
|
|
25690
|
+
var MENTION_NOTE = 'Prompt: {{var}} no template + data.mentions {var: {node, port}} (o !! de um Bloco \xE9 port "text") j\xE1 cria, no mesmo passo, a liga\xE7\xE3o fina de men\xE7\xE3o que o Canvas desenha; tirar a var ou a men\xE7\xE3o tira a liga\xE7\xE3o. ';
|
|
25268
25691
|
var MONTAGE_NOTE = "Montagem: musicGainDb, duckingDb e fadeMs valem s\xF3 para a montagem autom\xE1tica (clipes ligados) e n\xE3o mexem na timeline do editor (data.timeline; data.sequence \xE9 o legado); o volume dela fica nos clipes (gainDb, gainKeyframes, fades) e nas faixas (gainDb de cada faixa, master).";
|
|
25269
25692
|
function dataSegments(path) {
|
|
25270
25693
|
const p = path?.trim() ?? "";
|
|
@@ -25283,8 +25706,26 @@ function legacySequenceGuard(node, key) {
|
|
|
25283
25706
|
var SECTION_Z = -1e3;
|
|
25284
25707
|
var GAP = 80;
|
|
25285
25708
|
var boardId2 = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
|
|
25286
|
-
var
|
|
25709
|
+
var nodeIdArg2 = external_exports.string().min(1).max(128);
|
|
25287
25710
|
var point = external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() });
|
|
25711
|
+
var LABEL_TEXT_SIZES = ["titulo", "subtitulo", "corpo"];
|
|
25712
|
+
var dim = external_exports.number().finite().min(16).max(2e4);
|
|
25713
|
+
var sizeArg = external_exports.union([external_exports.object({ w: dim.optional(), h: dim.optional() }).strict(), external_exports.enum(LABEL_TEXT_SIZES)]).describe(
|
|
25714
|
+
'{w, h}: caixa do n\xF3 (piso: o cart\xE3o cresce com o conte\xFAdo; um lado s\xF3 vale). "titulo" | "subtitulo" | "corpo": tamanho do texto do Texto livre (label), gravado em data.size.'
|
|
25715
|
+
);
|
|
25716
|
+
var isLayoutOp = (op) => op.type === "batch" ? op.ops.every(isLayoutOp) : op.type === "node.set" && /^(position\.[xy]|size\.[wh])$/.test(op.path);
|
|
25717
|
+
function textSizeFor(kind, size) {
|
|
25718
|
+
if (kind !== "label") {
|
|
25719
|
+
throw new CanvasToolError(
|
|
25720
|
+
`size "${size}" \xE9 o tamanho do texto do Texto livre (label); para ${kind ?? "este n\xF3"} use size {w, h}.`
|
|
25721
|
+
);
|
|
25722
|
+
}
|
|
25723
|
+
return size;
|
|
25724
|
+
}
|
|
25725
|
+
function boxOf(size) {
|
|
25726
|
+
if (size.w === void 0 && size.h === void 0) throw new CanvasToolError("size {w, h}: informe w, h ou os dois.");
|
|
25727
|
+
return size;
|
|
25728
|
+
}
|
|
25288
25729
|
function registerCanvasTools(server, client, cfg) {
|
|
25289
25730
|
const api = new CanvasApi(client, cfg);
|
|
25290
25731
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -25308,7 +25749,7 @@ function registerCanvasTools(server, client, cfg) {
|
|
|
25308
25749
|
}
|
|
25309
25750
|
async function edit(id, build, opts = {}) {
|
|
25310
25751
|
const last = seen.get(id);
|
|
25311
|
-
if (last === void 0) {
|
|
25752
|
+
if (last === void 0 && !opts.layoutOnly) {
|
|
25312
25753
|
throw new CanvasToolError("Leia o board com canvas_read_board antes de editar (a leitura vale para a sess\xE3o).");
|
|
25313
25754
|
}
|
|
25314
25755
|
const snap = await api.snapshot(id);
|
|
@@ -25316,10 +25757,11 @@ function registerCanvasTools(server, client, cfg) {
|
|
|
25316
25757
|
const doc = snap.doc;
|
|
25317
25758
|
const { ops, done, targets } = await build(doc, snap);
|
|
25318
25759
|
if (!ops.length) return `Nada a mudar. ${done}`;
|
|
25760
|
+
if (opts.layoutOnly && !ops.every(isLayoutOp)) throw new Error("layout_only com opera\xE7\xE3o fora de posi\xE7\xE3o/tamanho");
|
|
25319
25761
|
let note = "";
|
|
25320
|
-
if (snap.seq > last) {
|
|
25762
|
+
if (!opts.layoutOnly && last !== void 0 && snap.seq > last) {
|
|
25321
25763
|
const since = await api.opsAfter(id, last);
|
|
25322
|
-
const theirs = since
|
|
25764
|
+
const theirs = since === null ? null : withoutMeasureNoise(since.filter((e) => e.clientId !== CLIENT_ID));
|
|
25323
25765
|
if (theirs === null) {
|
|
25324
25766
|
seen.delete(id);
|
|
25325
25767
|
throw new CanvasToolError(
|
|
@@ -25337,21 +25779,21 @@ function registerCanvasTools(server, client, cfg) {
|
|
|
25337
25779
|
if (theirs.length) note = `
|
|
25338
25780
|
Aviso: ${changesSince(theirs, doc)}.`;
|
|
25339
25781
|
}
|
|
25340
|
-
if (opts.split) return commitSplit(id, ops, done, note);
|
|
25782
|
+
if (opts.split) return commitSplit(id, ops, done, note, !!opts.layoutOnly);
|
|
25341
25783
|
const env = envelope(id, batchOf(ops), await api.me());
|
|
25342
25784
|
const bad = checkEnvelope(env);
|
|
25343
25785
|
if (bad) throw new CanvasToolError(`Edi\xE7\xE3o recusada antes de enviar: ${bad}.`);
|
|
25344
25786
|
const res = await api.commit(id, [env]);
|
|
25345
25787
|
const outcome = res.outcomes.find((o) => o.opId === env.id);
|
|
25346
25788
|
if (!outcome || "rejected" in outcome) {
|
|
25347
|
-
seen.delete(id);
|
|
25789
|
+
if (!opts.layoutOnly) seen.delete(id);
|
|
25348
25790
|
const why = outcome && "rejected" in outcome ? REJECT_TEXT[outcome.rejected] ?? outcome.rejected : "sem resposta do servidor";
|
|
25349
25791
|
throw new CanvasToolError(`O servidor recusou a edi\xE7\xE3o: ${why}. Nada foi aplicado. Releia com canvas_read_board.`);
|
|
25350
25792
|
}
|
|
25351
|
-
seen.set(id, res.seq);
|
|
25793
|
+
if (!opts.layoutOnly) seen.set(id, res.seq);
|
|
25352
25794
|
return `\u2705 ${done} (seq ${res.seq}).${note}`;
|
|
25353
25795
|
}
|
|
25354
|
-
async function commitSplit(id, ops, done, note) {
|
|
25796
|
+
async function commitSplit(id, ops, done, note, layoutOnly = false) {
|
|
25355
25797
|
let envs;
|
|
25356
25798
|
try {
|
|
25357
25799
|
envs = packEnvelopes(id, ops, await api.me());
|
|
@@ -25373,7 +25815,8 @@ Aviso: ${changesSince(theirs, doc)}.`;
|
|
|
25373
25815
|
for (const o of res.outcomes) outcomes.set(o.opId, o);
|
|
25374
25816
|
last = res.seq;
|
|
25375
25817
|
}
|
|
25376
|
-
const
|
|
25818
|
+
const setNodes = new Set(ops.flatMap((op) => op.type === "batch" ? op.ops : [op]).flatMap((op) => op.type === "node.set" ? [op.nodeId] : []));
|
|
25819
|
+
const pathsOf = (op) => op.type === "batch" ? op.ops.flatMap(pathsOf) : op.type === "node.set" ? [`${setNodes.size > 1 ? `${op.nodeId}:` : ""}${op.path.replace(/^data\./, "")}`] : [op.type];
|
|
25377
25820
|
let failed = 0;
|
|
25378
25821
|
const lines = envs.map((env, i) => {
|
|
25379
25822
|
const paths = pathsOf(env.op);
|
|
@@ -25384,14 +25827,14 @@ Aviso: ${changesSince(theirs, doc)}.`;
|
|
|
25384
25827
|
return `- envelope ${i + 1}/${envs.length}: ${paths.length} caminho(s) (${span}), ${kb} KB \u2192 ${result}`;
|
|
25385
25828
|
});
|
|
25386
25829
|
if (failed) {
|
|
25387
|
-
seen.delete(id);
|
|
25830
|
+
if (!layoutOnly) seen.delete(id);
|
|
25388
25831
|
throw new CanvasToolError(
|
|
25389
25832
|
`${done}: ${envs.length - failed} de ${envs.length} envelope(s) entraram, ${failed} recusado(s).
|
|
25390
25833
|
${lines.join("\n")}
|
|
25391
|
-
Os aceitos ficam. Releia com canvas_read_board e canvas_read_node e reenvie s\xF3 os caminhos dos envelopes recusados
|
|
25834
|
+
` + (layoutOnly ? "Os aceitos ficam. Rode de novo: posi\xE7\xE3o e tamanho n\xE3o pedem releitura." : "Os aceitos ficam. Releia com canvas_read_board e canvas_read_node e reenvie s\xF3 os caminhos dos envelopes recusados.") + note
|
|
25392
25835
|
);
|
|
25393
25836
|
}
|
|
25394
|
-
if (last !== null) seen.set(id, last);
|
|
25837
|
+
if (last !== null && !layoutOnly) seen.set(id, last);
|
|
25395
25838
|
return `\u2705 ${done} em ${envs.length} envelope(s) (seq at\xE9 ${last}).
|
|
25396
25839
|
${lines.join("\n")}${note}`;
|
|
25397
25840
|
}
|
|
@@ -25445,11 +25888,53 @@ ${lines.join("\n")}${note}`;
|
|
|
25445
25888
|
const out = {};
|
|
25446
25889
|
for (const [k, v] of Object.entries(raw ?? {})) {
|
|
25447
25890
|
if (k === KIND_FIELD) throw new CanvasToolError("data.kind n\xE3o muda: apague o n\xF3 e crie outro do tipo certo.");
|
|
25891
|
+
if (k === MEASURED_KEY) {
|
|
25892
|
+
throw new CanvasToolError(`data.${MEASURED_KEY} \xE9 o tamanho medido pela tela do Canvas: s\xF3 o Canvas grava. Para mudar a caixa, use size {w, h}.`);
|
|
25893
|
+
}
|
|
25448
25894
|
if (!isJson(v)) throw new CanvasToolError(`data.${k} n\xE3o \xE9 JSON v\xE1lido.`);
|
|
25449
25895
|
out[k] = v;
|
|
25450
25896
|
}
|
|
25451
25897
|
return out;
|
|
25452
25898
|
}
|
|
25899
|
+
function changeOps(node, change) {
|
|
25900
|
+
const ops = [];
|
|
25901
|
+
const patch = parseData(change.data);
|
|
25902
|
+
for (const key of Object.keys(patch)) legacySequenceGuard(node, key);
|
|
25903
|
+
if (change.label) patch.label = change.label;
|
|
25904
|
+
let box;
|
|
25905
|
+
if (typeof change.size === "string") patch.size = textSizeFor(nodeKind(node), change.size);
|
|
25906
|
+
else if (change.size) box = boxOf(change.size);
|
|
25907
|
+
const data = { ...node.data };
|
|
25908
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
25909
|
+
const path = `data.${k}`;
|
|
25910
|
+
if (v === null) {
|
|
25911
|
+
if (Object.hasOwn(node.data, k)) ops.push({ type: "node.set", nodeId: node.id, path, value: unsetValue() });
|
|
25912
|
+
delete data[k];
|
|
25913
|
+
} else if (JSON.stringify(node.data[k]) !== JSON.stringify(v)) {
|
|
25914
|
+
ops.push({ type: "node.set", nodeId: node.id, path, value: setValue(v) });
|
|
25915
|
+
data[k] = v;
|
|
25916
|
+
}
|
|
25917
|
+
}
|
|
25918
|
+
const set = (path, now, next) => {
|
|
25919
|
+
if (next !== void 0 && next !== now) ops.push({ type: "node.set", nodeId: node.id, path, value: setValue(next) });
|
|
25920
|
+
};
|
|
25921
|
+
set("position.x", node.position.x, change.position?.x);
|
|
25922
|
+
set("position.y", node.position.y, change.position?.y);
|
|
25923
|
+
set("size.w", node.size.w, box?.w);
|
|
25924
|
+
set("size.h", node.size.h, box?.h);
|
|
25925
|
+
const keys = ops.map((o) => o.type === "node.set" ? o.path.replace(/^data\./, "") : "");
|
|
25926
|
+
return { ops, keys, data, dataKeys: Object.keys(patch) };
|
|
25927
|
+
}
|
|
25928
|
+
async function mentionOps(doc, changed, newEdges = []) {
|
|
25929
|
+
const prompts = changed.filter((c) => nodeKind(c.node) === "prompt" && c.keys.some((k) => MENTION_KEYS.includes(k)));
|
|
25930
|
+
if (!prompts.length) return { ops: [], note: "" };
|
|
25931
|
+
const after = { ...doc, nodes: { ...doc.nodes }, edges: { ...doc.edges } };
|
|
25932
|
+
for (const c of changed) after.nodes[c.node.id] = { ...c.node, data: c.data };
|
|
25933
|
+
for (const e of newEdges) after.edges[e.id] = e;
|
|
25934
|
+
const sync = mentionEdgeOps(after, prompts.map((c) => c.node.id), await api.me());
|
|
25935
|
+
const name = (id) => after.nodes[id] ? `"${oneLine(nodeName(after.nodes[id]), 40)}"` : id;
|
|
25936
|
+
return { ops: sync.ops, note: describeMentionSync(sync, name) };
|
|
25937
|
+
}
|
|
25453
25938
|
server.registerTool(
|
|
25454
25939
|
"canvas_list_boards",
|
|
25455
25940
|
{
|
|
@@ -25468,10 +25953,10 @@ ${lines.join("\n")}${note}`;
|
|
|
25468
25953
|
"canvas_read_board",
|
|
25469
25954
|
{
|
|
25470
25955
|
title: "Canvas: ler board",
|
|
25471
|
-
description:
|
|
25956
|
+
description: 'L\xEA um board: n\xF3s (id, tipo, nome, posi\xE7\xE3o, tamanho, campos principais), liga\xE7\xF5es e v\xEDnculo com o IA Lab, sem m\xEDdia. Tamanho "medido" \xE9 o da tela; "gravado" \xE9 o piso (o cart\xE3o cresce com o conte\xFAdo). Obrigat\xF3rio antes de editar (vale para a sess\xE3o). node_ids devolve o data desses n\xF3s, com cada corte marcado (path=\u2026); o valor inteiro (ex.: data.sequence da Montagem) sai no canvas_read_node.',
|
|
25472
25957
|
inputSchema: {
|
|
25473
25958
|
board_id: boardId2,
|
|
25474
|
-
node_ids: external_exports.array(
|
|
25959
|
+
node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados).")
|
|
25475
25960
|
},
|
|
25476
25961
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
25477
25962
|
},
|
|
@@ -25493,7 +25978,7 @@ ${lines.join("\n")}${note}`;
|
|
|
25493
25978
|
description: 'Devolve o data de UM n\xF3 sem corte nenhum, em JSON compacto, para conferir e fazer diff. Na Montagem, sem path, vem a timeline do editor: data.timeline, ou data.sequence (legado) quando o n\xF3 ainda n\xE3o tem timeline, e o cabe\xE7alho diz qual veio. path escolhe um peda\xE7o ("items" da M\xEDdia, "timeline.meta"); "data" traz o data todo. Diz o tamanho total e o sha256; acima de max_chars vem em partes expl\xEDcitas (offset). N\xE3o substitui o canvas_read_board, que continua obrigat\xF3rio antes de editar. ' + MONTAGE_NOTE,
|
|
25494
25979
|
inputSchema: {
|
|
25495
25980
|
board_id: boardId2,
|
|
25496
|
-
node_id:
|
|
25981
|
+
node_id: nodeIdArg2,
|
|
25497
25982
|
path: external_exports.string().trim().max(200).optional().describe('Caminho dentro do data, com pontos (ex.: "timeline.meta", "items.0"). "data" = o data todo.'),
|
|
25498
25983
|
offset: external_exports.number().int().min(0).optional().describe("In\xEDcio da parte, em caracteres (padr\xE3o 0)."),
|
|
25499
25984
|
max_chars: external_exports.number().int().min(1e3).max(6e4).optional().describe("Tamanho da parte (padr\xE3o 40000).")
|
|
@@ -25504,8 +25989,11 @@ ${lines.join("\n")}${note}`;
|
|
|
25504
25989
|
const snap = await api.snapshot(board_id);
|
|
25505
25990
|
const node = snap.doc.nodes[node_id];
|
|
25506
25991
|
if (!node) throw new CanvasToolError(`N\xF3 ${node_id} n\xE3o existe no board. Releia com canvas_read_board.`);
|
|
25507
|
-
let value = node.data;
|
|
25992
|
+
let value = visibleData(node.data);
|
|
25508
25993
|
let segments = dataSegments(path);
|
|
25994
|
+
if (segments[0] === MEASURED_KEY) {
|
|
25995
|
+
throw new CanvasToolError(`data.${MEASURED_KEY} \xE9 interno do Canvas (tamanho medido da tela): ele sai no cabe\xE7alho desta leitura.`);
|
|
25996
|
+
}
|
|
25509
25997
|
let which = "";
|
|
25510
25998
|
if (!path?.trim() && nodeKind(node) === "montage") {
|
|
25511
25999
|
if (node.data.timeline !== void 0) {
|
|
@@ -25522,9 +26010,9 @@ ${lines.join("\n")}${note}`;
|
|
|
25522
26010
|
const here = value;
|
|
25523
26011
|
value = Array.isArray(here) ? here[Number(seg)] : here && typeof here === "object" ? here[seg] : void 0;
|
|
25524
26012
|
if (value === void 0) {
|
|
25525
|
-
const
|
|
26013
|
+
const at2 = segments.slice(0, i).join(".");
|
|
25526
26014
|
const keys = Array.isArray(here) ? `\xEDndices 0 a ${here.length - 1}` : here && typeof here === "object" ? Object.keys(here).join(", ") : "\u2014";
|
|
25527
|
-
throw new CanvasToolError(`"${seg}" n\xE3o existe em data${
|
|
26015
|
+
throw new CanvasToolError(`"${seg}" n\xE3o existe em data${at2 ? `.${at2}` : ""}. Existe: ${keys || "\u2014"}.`);
|
|
25528
26016
|
}
|
|
25529
26017
|
}
|
|
25530
26018
|
const json2 = JSON.stringify(value);
|
|
@@ -25534,7 +26022,8 @@ ${lines.join("\n")}${note}`;
|
|
|
25534
26022
|
const to = Math.min(from + size, total);
|
|
25535
26023
|
const hash = createHash("sha256").update(json2).digest("hex").slice(0, 12);
|
|
25536
26024
|
const where = `data${segments.length ? `.${segments.join(".")}` : ""}`;
|
|
25537
|
-
const
|
|
26025
|
+
const at = readMeasured(node)?.at;
|
|
26026
|
+
const head = `${node.id} \xB7 ${nodeKind(node) ?? node.type} \xB7 "${oneLine(nodeName(node), 60)}" \xB7 board seq ${snap.seq} \xB7 tamanho ${sizeLabel(node)}${at ? ` \xB7 medido em ${at}` : ""} \xB7 ${where}${which}: ${total} caracteres (JSON compacto) \xB7 sha256 ${hash}`;
|
|
25538
26027
|
const part = from === 0 && to === total ? "Inteiro, abaixo." : `Parte: caracteres ${from} a ${to} de ${total}. ${to < total ? `Falta o resto: chame de novo com offset=${to}.` : "Esta \xE9 a \xFAltima parte."}`;
|
|
25539
26028
|
return `${head}
|
|
25540
26029
|
${part}
|
|
@@ -25595,15 +26084,16 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25595
26084
|
"canvas_add_node",
|
|
25596
26085
|
{
|
|
25597
26086
|
title: "Canvas: adicionar n\xF3",
|
|
25598
|
-
description: `Adiciona um n\xF3 (n\xE3o executa). ${READ}kind: ${kindList()}. data completa o padr\xE3o do tipo; label d\xE1 o nome. Sem position, nasce ao lado do n\xF3 ligado ou \xE0 direita do board. inputs/outputs j\xE1 ligam o n\xF3 no mesmo passo. ` + MONTAGE_NOTE,
|
|
26087
|
+
description: `Adiciona um n\xF3 (n\xE3o executa). ${READ}kind: ${kindList()}. data completa o padr\xE3o do tipo; label d\xE1 o nome. Sem position, nasce ao lado do n\xF3 ligado ou \xE0 direita do board. inputs/outputs j\xE1 ligam o n\xF3 no mesmo passo. ` + MENTION_NOTE + MONTAGE_NOTE,
|
|
25599
26088
|
inputSchema: {
|
|
25600
26089
|
board_id: boardId2,
|
|
25601
26090
|
kind: external_exports.string().min(1).max(40),
|
|
25602
26091
|
label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
|
|
25603
26092
|
data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
|
|
25604
26093
|
position: point.optional(),
|
|
25605
|
-
|
|
25606
|
-
|
|
26094
|
+
size: sizeArg.optional(),
|
|
26095
|
+
inputs: external_exports.array(external_exports.object({ from: nodeIdArg2, from_port: external_exports.string().min(1), to_port: external_exports.string().min(1) })).max(20).optional().describe("Liga\xE7\xF5es que CHEGAM no n\xF3 novo: from.from_port \u2192 novo.to_port."),
|
|
26096
|
+
outputs: external_exports.array(external_exports.object({ from_port: external_exports.string().min(1), to: nodeIdArg2, to_port: external_exports.string().min(1) })).max(20).optional().describe("Liga\xE7\xF5es que SAEM do n\xF3 novo: novo.from_port \u2192 to.to_port.")
|
|
25607
26097
|
},
|
|
25608
26098
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
25609
26099
|
},
|
|
@@ -25614,10 +26104,16 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25614
26104
|
const me = await api.me();
|
|
25615
26105
|
const data = { ...spec.data, ...parseData(args.data), [KIND_FIELD]: args.kind };
|
|
25616
26106
|
if (args.label) data.label = args.label;
|
|
26107
|
+
let box = spec.size;
|
|
26108
|
+
if (typeof args.size === "string") data.size = textSizeFor(args.kind, args.size);
|
|
26109
|
+
else if (args.size) {
|
|
26110
|
+
const b = boxOf(args.size);
|
|
26111
|
+
box = { w: b.w ?? spec.size.w, h: b.h ?? spec.size.h };
|
|
26112
|
+
}
|
|
25617
26113
|
const near = args.inputs?.[0] ? doc.nodes[args.inputs[0].from] : args.outputs?.[0] ? doc.nodes[args.outputs[0].to] : void 0;
|
|
25618
|
-
const position = args.position ?? placeFor(doc,
|
|
26114
|
+
const position = args.position ?? placeFor(doc, box, near, args.inputs?.[0] ? "right" : "left");
|
|
25619
26115
|
const zIndex = args.kind === "section" ? SECTION_Z : Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1;
|
|
25620
|
-
const node = makeNode({ type: WORKFLOW_NODE_TYPE, data, position, size:
|
|
26116
|
+
const node = makeNode({ type: WORKFLOW_NODE_TYPE, data, position, size: box, zIndex, createdBy: me });
|
|
25621
26117
|
const links = [
|
|
25622
26118
|
...(args.inputs ?? []).map((l) => ({ source: l.from, sourcePort: l.from_port, target: node.id, targetPort: l.to_port })),
|
|
25623
26119
|
...(args.outputs ?? []).map((l) => ({ source: node.id, sourcePort: l.from_port, target: l.to, targetPort: l.to_port }))
|
|
@@ -25631,10 +26127,12 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25631
26127
|
}
|
|
25632
26128
|
}
|
|
25633
26129
|
const edges = edgeOps(doc, links, me, /* @__PURE__ */ new Set([node.id]));
|
|
26130
|
+
const drawn = edges.ops.flatMap((op) => op.type === "edge.add" ? [op.edge] : []);
|
|
26131
|
+
const mention = await mentionOps(doc, [{ node, data: node.data, keys: Object.keys(args.data ?? {}) }], drawn);
|
|
25634
26132
|
return {
|
|
25635
|
-
ops: [{ type: "node.add", nodeId: node.id, node }, ...edges.ops],
|
|
26133
|
+
ops: [{ type: "node.add", nodeId: node.id, node }, ...edges.ops, ...mention.ops],
|
|
25636
26134
|
targets: links.map((l) => l.source === node.id ? l.target : l.source),
|
|
25637
|
-
done: `N\xF3 ${args.kind} "${nodeName(node)}" criado: ${node.id}${edges.ops.length ? ` com ${edges.ops.length} liga\xE7\xE3o(\xF5es)` : ""}.${edges.warn}`
|
|
26135
|
+
done: `N\xF3 ${args.kind} "${nodeName(node)}" criado: ${node.id}${edges.ops.length ? ` com ${edges.ops.length} liga\xE7\xE3o(\xF5es)` : ""}.${edges.warn}${mention.note}`
|
|
25638
26136
|
};
|
|
25639
26137
|
})
|
|
25640
26138
|
)
|
|
@@ -25643,48 +26141,90 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25643
26141
|
"canvas_update_node",
|
|
25644
26142
|
{
|
|
25645
26143
|
title: "Canvas: editar n\xF3",
|
|
25646
|
-
description: `Muda campos do data de um n\xF3, chave a chave (null apaga a chave), sem executar. ${READ}label renomeia. N\xE3o troca o tipo. Cada chave \xE9 gravada inteira: leia antes com canvas_read_node e mande o valor completo j\xE1 revisado, para n\xE3o apagar o que outra pessoa escreveu. Para mudar partes de uma estrutura grande (clipes da timeline), use canvas_patch_node, por caminho. ` + MONTAGE_NOTE,
|
|
26144
|
+
description: `Muda campos do data de um n\xF3, chave a chave (null apaga a chave), sem executar. ${READ}label renomeia; position move; size {w, h} muda a caixa (piso: o cart\xE3o cresce com o conte\xFAdo). N\xE3o troca o tipo. Cada chave \xE9 gravada inteira: leia antes com canvas_read_node e mande o valor completo j\xE1 revisado, para n\xE3o apagar o que outra pessoa escreveu. Para mudar partes de uma estrutura grande (clipes da timeline), use canvas_patch_node, por caminho; v\xE1rios n\xF3s de uma vez, canvas_update_nodes. ` + MENTION_NOTE + MONTAGE_NOTE,
|
|
25647
26145
|
inputSchema: {
|
|
25648
26146
|
board_id: boardId2,
|
|
25649
|
-
node_id:
|
|
26147
|
+
node_id: nodeIdArg2,
|
|
25650
26148
|
data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave."),
|
|
25651
26149
|
label: external_exports.string().trim().min(1).max(120).optional(),
|
|
25652
|
-
position: point.optional()
|
|
26150
|
+
position: point.optional(),
|
|
26151
|
+
size: sizeArg.optional()
|
|
25653
26152
|
},
|
|
25654
26153
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
25655
26154
|
},
|
|
25656
26155
|
async (args) => run(
|
|
25657
26156
|
() => edit(args.board_id, async (doc) => {
|
|
25658
26157
|
const node = needNode(doc, args.node_id);
|
|
25659
|
-
const
|
|
25660
|
-
const
|
|
25661
|
-
|
|
25662
|
-
|
|
25663
|
-
|
|
25664
|
-
|
|
25665
|
-
|
|
25666
|
-
if (Object.hasOwn(node.data, k)) ops.push({ type: "node.set", nodeId: node.id, path, value: unsetValue() });
|
|
25667
|
-
} else if (JSON.stringify(node.data[k]) !== JSON.stringify(v)) {
|
|
25668
|
-
ops.push({ type: "node.set", nodeId: node.id, path, value: setValue(v) });
|
|
25669
|
-
}
|
|
25670
|
-
}
|
|
25671
|
-
if (args.position) {
|
|
25672
|
-
ops.push({ type: "node.set", nodeId: node.id, path: "position.x", value: setValue(args.position.x) });
|
|
25673
|
-
ops.push({ type: "node.set", nodeId: node.id, path: "position.y", value: setValue(args.position.y) });
|
|
25674
|
-
}
|
|
25675
|
-
const keys = ops.map((o) => o.type === "node.set" ? o.path.replace(/^data\./, "") : "").join(", ");
|
|
25676
|
-
return { ops, targets: [node.id], done: `N\xF3 "${nodeName(node)}" atualizado: ${keys || "nada mudou"}` };
|
|
26158
|
+
const c = changeOps(node, args);
|
|
26159
|
+
const mention = await mentionOps(doc, [{ node, data: c.data, keys: c.dataKeys }]);
|
|
26160
|
+
return {
|
|
26161
|
+
ops: [...c.ops, ...mention.ops],
|
|
26162
|
+
targets: [node.id],
|
|
26163
|
+
done: `N\xF3 "${nodeName(node)}" atualizado: ${c.keys.join(", ") || "nada mudou"}${mention.note}`
|
|
26164
|
+
};
|
|
25677
26165
|
})
|
|
25678
26166
|
)
|
|
25679
26167
|
);
|
|
26168
|
+
server.registerTool(
|
|
26169
|
+
"canvas_update_nodes",
|
|
26170
|
+
{
|
|
26171
|
+
title: "Canvas: editar v\xE1rios n\xF3s",
|
|
26172
|
+
description: 'Muda v\xE1rios n\xF3s num passo s\xF3, cada um como no canvas_update_node (position, size, label, data com null apagando). layout_only: true aceita s\xF3 position e size {w, h}, n\xE3o exige leitura e n\xE3o recusa por "outra pessoa mexeu" (nesses dois campos vale quem grava por \xFAltimo). Sem layout_only, exige canvas_read_board nesta sess\xE3o e recusa se outra pessoa mexeu em algum desses n\xF3s. Quebra em envelopes de at\xE9 64 KB e relata cada um. Para arrumar linhas inteiras (t\xEDtulo, prompt, refer\xEAncia, gerador), use canvas_arrange_rows.',
|
|
26173
|
+
inputSchema: {
|
|
26174
|
+
board_id: boardId2,
|
|
26175
|
+
nodes: external_exports.array(
|
|
26176
|
+
external_exports.object({
|
|
26177
|
+
node_id: nodeIdArg2,
|
|
26178
|
+
position: point.optional(),
|
|
26179
|
+
size: sizeArg.optional(),
|
|
26180
|
+
label: external_exports.string().trim().min(1).max(120).optional(),
|
|
26181
|
+
data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave.")
|
|
26182
|
+
}).strict()
|
|
26183
|
+
).min(1).max(200),
|
|
26184
|
+
layout_only: external_exports.boolean().optional().describe("true: s\xF3 position e size, sem releitura nem recusa por conflito.")
|
|
26185
|
+
},
|
|
26186
|
+
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
26187
|
+
},
|
|
26188
|
+
async (args) => run(
|
|
26189
|
+
() => edit(
|
|
26190
|
+
args.board_id,
|
|
26191
|
+
async (doc) => {
|
|
26192
|
+
const ids = args.nodes.map((n) => n.node_id);
|
|
26193
|
+
const dup = ids.find((id, i) => ids.indexOf(id) !== i);
|
|
26194
|
+
if (dup) throw new CanvasToolError(`O n\xF3 ${dup} aparece duas vezes: junte as mudan\xE7as dele num item s\xF3.`);
|
|
26195
|
+
if (args.layout_only) {
|
|
26196
|
+
const bad = args.nodes.find((n) => n.data !== void 0 || n.label !== void 0 || typeof n.size === "string");
|
|
26197
|
+
if (bad) {
|
|
26198
|
+
throw new CanvasToolError(
|
|
26199
|
+
`layout_only aceita s\xF3 position e size {w, h} (o n\xF3 ${bad.node_id} traz ${bad.data !== void 0 ? "data" : bad.label !== void 0 ? "label" : `size "${String(bad.size)}"`}). Tire layout_only para gravar data e label com a checagem de conflito.`
|
|
26200
|
+
);
|
|
26201
|
+
}
|
|
26202
|
+
}
|
|
26203
|
+
const changes = args.nodes.map((n) => {
|
|
26204
|
+
const node = needNode(doc, n.node_id);
|
|
26205
|
+
return { node, ...changeOps(node, n) };
|
|
26206
|
+
});
|
|
26207
|
+
const mention = args.layout_only ? { ops: [], note: "" } : await mentionOps(doc, changes.map((c) => ({ node: c.node, data: c.data, keys: c.dataKeys })));
|
|
26208
|
+
const touched = changes.filter((c) => c.ops.length);
|
|
26209
|
+
const list = touched.slice(0, 12).map((c) => `"${oneLine(nodeName(c.node), 30)}" (${c.keys.join(", ")})`).join("; ");
|
|
26210
|
+
return {
|
|
26211
|
+
ops: [...changes.flatMap((c) => c.ops), ...mention.ops],
|
|
26212
|
+
targets: ids,
|
|
26213
|
+
done: `${touched.length} de ${ids.length} n\xF3(s) atualizado(s)${args.layout_only ? " (layout_only)" : ""}` + (list ? `: ${list}${touched.length > 12 ? `; \u2026 +${touched.length - 12}` : ""}` : "") + mention.note
|
|
26214
|
+
};
|
|
26215
|
+
},
|
|
26216
|
+
{ split: true, layoutOnly: !!args.layout_only }
|
|
26217
|
+
)
|
|
26218
|
+
)
|
|
26219
|
+
);
|
|
25680
26220
|
server.registerTool(
|
|
25681
26221
|
"canvas_patch_node",
|
|
25682
26222
|
{
|
|
25683
26223
|
title: "Canvas: gravar por caminho",
|
|
25684
|
-
description: `Grava v\xE1rios caminhos dentro do data de um n\xF3, sem executar: {path, value} grava, {path, unset: true} apaga. ${READ}O caminho atravessa objetos (o n\xEDvel que falta \xE9 criado) e n\xE3o entra em lista: para mudar um item de lista, grave a lista inteira. Quebra sozinho em envelopes de at\xE9 64 KB, em ordem, e devolve o resultado de cada um; entre envelopes n\xE3o h\xE1 atomicidade (o que entrou fica). Serve para mexer em partes de uma estrutura grande, como clipes da timeline da Montagem, sem reenviar tudo. Leia antes com canvas_read_node. ` + MONTAGE_NOTE,
|
|
26224
|
+
description: `Grava v\xE1rios caminhos dentro do data de um n\xF3, sem executar: {path, value} grava, {path, unset: true} apaga. ${READ}O caminho atravessa objetos (o n\xEDvel que falta \xE9 criado) e n\xE3o entra em lista: para mudar um item de lista, grave a lista inteira. Quebra sozinho em envelopes de at\xE9 64 KB, em ordem, e devolve o resultado de cada um; entre envelopes n\xE3o h\xE1 atomicidade (o que entrou fica). Serve para mexer em partes de uma estrutura grande, como clipes da timeline da Montagem, sem reenviar tudo. Leia antes com canvas_read_node. ` + MENTION_NOTE + MONTAGE_NOTE,
|
|
25685
26225
|
inputSchema: {
|
|
25686
26226
|
board_id: boardId2,
|
|
25687
|
-
node_id:
|
|
26227
|
+
node_id: nodeIdArg2,
|
|
25688
26228
|
sets: external_exports.array(
|
|
25689
26229
|
external_exports.object({
|
|
25690
26230
|
path: external_exports.string().trim().min(1).max(500).describe('Caminho dentro do data (ex.: "timeline.meta.fps"); aceita o prefixo "data.".'),
|
|
@@ -25704,6 +26244,8 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25704
26244
|
const ops = [];
|
|
25705
26245
|
let same = 0;
|
|
25706
26246
|
let absent = 0;
|
|
26247
|
+
const mentionKeys = /* @__PURE__ */ new Set();
|
|
26248
|
+
let mentionAt = -1;
|
|
25707
26249
|
for (const [i, item] of args.sets.entries()) {
|
|
25708
26250
|
const at = `sets[${i}] (${item.path})`;
|
|
25709
26251
|
const segs = dataSegments(item.path);
|
|
@@ -25713,6 +26255,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25713
26255
|
const path = `data.${segs.join(".")}`;
|
|
25714
26256
|
const badPath = checkNodePath(path);
|
|
25715
26257
|
if (badPath) throw new CanvasToolError(`${at}: ${badPath}.`);
|
|
26258
|
+
if (MENTION_KEYS.includes(segs[0])) mentionKeys.add(segs[0]);
|
|
25716
26259
|
const unset = item.unset === true;
|
|
25717
26260
|
const hasValue = item.value !== void 0;
|
|
25718
26261
|
if (unset === hasValue) throw new CanvasToolError(`${at}: mande value OU unset: true.`);
|
|
@@ -25747,6 +26290,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25747
26290
|
}
|
|
25748
26291
|
delete cur[leaf];
|
|
25749
26292
|
ops.push({ type: "node.set", nodeId: node.id, path, value: unsetValue() });
|
|
26293
|
+
if (MENTION_KEYS.includes(segs[0])) mentionAt = ops.length;
|
|
25750
26294
|
continue;
|
|
25751
26295
|
}
|
|
25752
26296
|
const value = item.value;
|
|
@@ -25756,12 +26300,16 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25756
26300
|
}
|
|
25757
26301
|
if (cur) cur[leaf] = value;
|
|
25758
26302
|
ops.push({ type: "node.set", nodeId: node.id, path, value: setValue(value) });
|
|
26303
|
+
if (MENTION_KEYS.includes(segs[0])) mentionAt = ops.length;
|
|
25759
26304
|
}
|
|
25760
26305
|
const skipped = [same ? `${same} igual(is) ao que j\xE1 estava` : "", absent ? `${absent} unset de caminho que n\xE3o existe` : ""].filter(Boolean).join(", ");
|
|
26306
|
+
const written = ops.length;
|
|
26307
|
+
const mention = await mentionOps(doc, [{ node, data: work, keys: [...mentionKeys] }]);
|
|
26308
|
+
ops.splice(mentionAt < 0 ? ops.length : mentionAt, 0, ...mention.ops);
|
|
25761
26309
|
return {
|
|
25762
26310
|
ops,
|
|
25763
26311
|
targets: [node.id],
|
|
25764
|
-
done: `N\xF3 "${oneLine(nodeName(node), 40)}" (${node.id}): ${
|
|
26312
|
+
done: `N\xF3 "${oneLine(nodeName(node), 40)}" (${node.id}): ${written} caminho(s) gravado(s)` + (skipped ? ` (ignorados: ${skipped})` : "") + mention.note
|
|
25765
26313
|
};
|
|
25766
26314
|
},
|
|
25767
26315
|
{ split: true }
|
|
@@ -25775,9 +26323,9 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25775
26323
|
description: `Liga a sa\xEDda de um n\xF3 \xE0 entrada de outro (ids e portas de canvas_read_board). ${READ}Liga\xE7\xE3o repetida \xE9 ignorada.`,
|
|
25776
26324
|
inputSchema: {
|
|
25777
26325
|
board_id: boardId2,
|
|
25778
|
-
source:
|
|
26326
|
+
source: nodeIdArg2,
|
|
25779
26327
|
source_port: external_exports.string().min(1).max(80),
|
|
25780
|
-
target:
|
|
26328
|
+
target: nodeIdArg2,
|
|
25781
26329
|
target_port: external_exports.string().min(1).max(80)
|
|
25782
26330
|
},
|
|
25783
26331
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
@@ -25802,8 +26350,8 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25802
26350
|
description: `Apaga n\xF3s (com as liga\xE7\xF5es e varia\xE7\xF5es deles) e/ou liga\xE7\xF5es, num passo s\xF3. Irrevers\xEDvel pelo MCP: confirme com o usu\xE1rio antes. ${READ}N\xE3o apaga o board.`,
|
|
25803
26351
|
inputSchema: {
|
|
25804
26352
|
board_id: boardId2,
|
|
25805
|
-
node_ids: external_exports.array(
|
|
25806
|
-
edge_ids: external_exports.array(
|
|
26353
|
+
node_ids: external_exports.array(nodeIdArg2).max(200).optional(),
|
|
26354
|
+
edge_ids: external_exports.array(nodeIdArg2).max(200).optional()
|
|
25807
26355
|
},
|
|
25808
26356
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
25809
26357
|
},
|
|
@@ -25904,13 +26452,14 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
25904
26452
|
);
|
|
25905
26453
|
registerCanvasCommentTools(server, api, run);
|
|
25906
26454
|
registerCanvasMediaTools(server, { api, cfg, run, edit, seen, placeFor });
|
|
26455
|
+
registerCanvasLayoutTools(server, { api, run, edit });
|
|
25907
26456
|
}
|
|
25908
26457
|
|
|
25909
26458
|
// src/version.ts
|
|
25910
26459
|
import { createRequire } from "module";
|
|
25911
26460
|
var require2 = createRequire(import.meta.url);
|
|
25912
26461
|
function readVersion() {
|
|
25913
|
-
if ("0.
|
|
26462
|
+
if ("0.29.0") return "0.29.0";
|
|
25914
26463
|
try {
|
|
25915
26464
|
const pkg = require2("../package.json");
|
|
25916
26465
|
return pkg.version ?? "0.0.0-unknown";
|
|
@@ -25926,6 +26475,7 @@ var SERVER_INSTRUCTIONS = [
|
|
|
25926
26475
|
'M\xEDdia: generate_* gasta diamantes; confira com estimate_cost antes de lotes grandes. Sem project, a gera\xE7\xE3o cai no projeto ativo; billing:"personal" cobra do usu\xE1rio.',
|
|
25927
26476
|
"Calls: search_calls acha, get_call traz a transcri\xE7\xE3o.",
|
|
25928
26477
|
"Canvas online (canvas_*): leia com canvas_read_board antes de editar e siga o estado lido. Cada edi\xE7\xE3o vai num passo s\xF3; se outra pessoa mexeu nos mesmos n\xF3s, a ferramenta recusa e pede releitura. As ferramentas canvas_* n\xE3o geram m\xEDdia nem gastam diamantes: quem roda os n\xF3s \xE9 o usu\xE1rio no Canvas. D\xEA nome (label) aos n\xF3s, n\xE3o invente tipos nem portas, e confirme antes de canvas_delete.",
|
|
26478
|
+
'Arrumar o board: o size gravado \xE9 piso (o cart\xE3o cresce com o conte\xFAdo); use o tamanho "medido" do canvas_read_board. \xC1rea de revis\xE3o: canvas_arrange_rows (uma linha por vers\xE3o); posi\xE7\xE3o/tamanho em lote: canvas_update_nodes com layout_only. N\xE3o afirme que nada se sobrep\xF5e com n\xF3 sem medida.',
|
|
25929
26479
|
'"Aplica os coment\xE1rios" do board: canvas_list_comments (abertos) \u2192 para cada um, leia o n\xF3 ancorado com canvas_read_board (node_ids), aplique a edi\xE7\xE3o pelas canvas_* e feche com canvas_resolve_comment dizendo em uma frase o que fez. Coment\xE1rio amb\xEDguo ou com alvo sumido: responda com pergunta (canvas_reply_comment) e n\xE3o resolva. D\xFAvida sua sobre um n\xF3: canvas_comment ancorado nele. As escritas j\xE1 saem espa\xE7adas (60/min); se ainda vier limite, pare e diga de onde seguir.'
|
|
25930
26480
|
].join("\n");
|
|
25931
26481
|
function buildServer(cfg) {
|
|
@@ -26020,7 +26570,7 @@ function serversNaming(entry) {
|
|
|
26020
26570
|
if (!servers || typeof servers !== "object") return [];
|
|
26021
26571
|
return Object.entries(servers).filter(([name, value]) => name === "beplus" || JSON.stringify(value ?? null).includes("beplus-mcp")).map(([name]) => name);
|
|
26022
26572
|
}
|
|
26023
|
-
function checkConfigs(
|
|
26573
|
+
function checkConfigs(report2) {
|
|
26024
26574
|
let found = 0;
|
|
26025
26575
|
for (const { path, client } of configCandidates()) {
|
|
26026
26576
|
let raw;
|
|
@@ -26033,7 +26583,7 @@ function checkConfigs(report) {
|
|
|
26033
26583
|
try {
|
|
26034
26584
|
parsed = JSON.parse(raw);
|
|
26035
26585
|
} catch {
|
|
26036
|
-
|
|
26586
|
+
report2.line("aviso", `Config ilegivel: ${path}`, "O arquivo existe mas nao e JSON valido.");
|
|
26037
26587
|
continue;
|
|
26038
26588
|
}
|
|
26039
26589
|
const names = new Set(serversNaming(parsed));
|
|
@@ -26043,25 +26593,25 @@ function checkConfigs(report) {
|
|
|
26043
26593
|
}
|
|
26044
26594
|
if (names.size === 0) continue;
|
|
26045
26595
|
found += 1;
|
|
26046
|
-
|
|
26596
|
+
report2.line("ok", `Config detectado: ${path}`, `${client}, servidor ${[...names].join(", ")}`);
|
|
26047
26597
|
}
|
|
26048
26598
|
if (found === 0) {
|
|
26049
|
-
|
|
26599
|
+
report2.line(
|
|
26050
26600
|
"aviso",
|
|
26051
26601
|
"Nenhum config de cliente MCP com o beplus foi encontrado.",
|
|
26052
26602
|
"Isso e normal se voce ainda nao instalou o servidor, ou se o cliente guarda o config em outro lugar."
|
|
26053
26603
|
);
|
|
26054
26604
|
}
|
|
26055
26605
|
}
|
|
26056
|
-
async function checkApiReachable(
|
|
26606
|
+
async function checkApiReachable(report2, baseUrl) {
|
|
26057
26607
|
const controller = new AbortController();
|
|
26058
26608
|
const timer = setTimeout(() => controller.abort(), 1e4);
|
|
26059
26609
|
try {
|
|
26060
26610
|
const res = await fetch(`${baseUrl}/api/v1/mcp/me`, { signal: controller.signal });
|
|
26061
|
-
|
|
26611
|
+
report2.line("ok", `API responde: ${baseUrl}`, `GET /api/v1/mcp/me sem token devolveu HTTP ${res.status}.`);
|
|
26062
26612
|
} catch (e) {
|
|
26063
26613
|
const aborted2 = e?.name === "AbortError";
|
|
26064
|
-
|
|
26614
|
+
report2.line(
|
|
26065
26615
|
"erro",
|
|
26066
26616
|
`API nao respondeu: ${baseUrl}`,
|
|
26067
26617
|
aborted2 ? "Tempo esgotado em 10s. Confira a rede, o proxy ou a VPN." : `Falha de rede: ${e?.message ?? "desconhecida"}. Confira a rede e o valor de BEPLUS_API_URL.`
|
|
@@ -26070,10 +26620,10 @@ async function checkApiReachable(report, baseUrl) {
|
|
|
26070
26620
|
clearTimeout(timer);
|
|
26071
26621
|
}
|
|
26072
26622
|
}
|
|
26073
|
-
async function checkToken(
|
|
26623
|
+
async function checkToken(report2) {
|
|
26074
26624
|
const token = process.env.BEPLUS_API_TOKEN?.trim();
|
|
26075
26625
|
if (!token) {
|
|
26076
|
-
|
|
26626
|
+
report2.line(
|
|
26077
26627
|
"aviso",
|
|
26078
26628
|
"BEPLUS_API_TOKEN nao esta definido.",
|
|
26079
26629
|
"Gere um Personal Access Token em Tokens de acesso, no painel BePlus, e coloque no config do MCP."
|
|
@@ -26082,46 +26632,46 @@ async function checkToken(report) {
|
|
|
26082
26632
|
}
|
|
26083
26633
|
try {
|
|
26084
26634
|
const me = await new BeplusClient(loadConfig()).me();
|
|
26085
|
-
|
|
26635
|
+
report2.line(
|
|
26086
26636
|
"ok",
|
|
26087
26637
|
`Token valido (${maskToken(token)})`,
|
|
26088
26638
|
`Conta: ${me.name ?? me.email ?? me.userId}, ${me.diamonds} diamantes.`
|
|
26089
26639
|
);
|
|
26090
26640
|
} catch (e) {
|
|
26091
26641
|
if (e instanceof ApiError && e.httpStatus === 401) {
|
|
26092
|
-
|
|
26642
|
+
report2.line(
|
|
26093
26643
|
"erro",
|
|
26094
26644
|
`Token invalido ou revogado (${maskToken(token)})`,
|
|
26095
26645
|
"Gere um novo Personal Access Token no painel BePlus e atualize BEPLUS_API_TOKEN no config do MCP."
|
|
26096
26646
|
);
|
|
26097
26647
|
return;
|
|
26098
26648
|
}
|
|
26099
|
-
|
|
26649
|
+
report2.line("aviso", "Nao deu para validar o token agora.", e?.message ?? "erro desconhecido");
|
|
26100
26650
|
}
|
|
26101
26651
|
}
|
|
26102
26652
|
async function runDoctor() {
|
|
26103
|
-
const
|
|
26653
|
+
const report2 = new Report();
|
|
26104
26654
|
process.stdout.write(`beplus-mcp doctor, versao ${VERSION}
|
|
26105
26655
|
|
|
26106
26656
|
`);
|
|
26107
26657
|
if (isSupportedNode()) {
|
|
26108
|
-
|
|
26658
|
+
report2.line("ok", `Node ${process.versions.node}`, `Minimo exigido: Node ${MIN_NODE_MAJOR}.`);
|
|
26109
26659
|
} else {
|
|
26110
|
-
|
|
26660
|
+
report2.line("erro", `Node ${process.versions.node}`, unsupportedNodeMessage());
|
|
26111
26661
|
}
|
|
26112
26662
|
let baseUrl = null;
|
|
26113
26663
|
try {
|
|
26114
26664
|
baseUrl = resolveBaseUrl();
|
|
26115
|
-
|
|
26665
|
+
report2.line("ok", `BEPLUS_API_URL: ${baseUrl}`);
|
|
26116
26666
|
} catch (e) {
|
|
26117
|
-
|
|
26667
|
+
report2.line("erro", "BEPLUS_API_URL invalida", e.message);
|
|
26118
26668
|
}
|
|
26119
26669
|
if (baseUrl) {
|
|
26120
|
-
await checkApiReachable(
|
|
26121
|
-
await checkToken(
|
|
26670
|
+
await checkApiReachable(report2, baseUrl);
|
|
26671
|
+
await checkToken(report2);
|
|
26122
26672
|
}
|
|
26123
|
-
checkConfigs(
|
|
26124
|
-
const code =
|
|
26673
|
+
checkConfigs(report2);
|
|
26674
|
+
const code = report2.exitCode;
|
|
26125
26675
|
process.stdout.write(code === 0 ? "\nTudo certo.\n" : "\nTem item com [erro] acima. Resolva antes de usar o MCP.\n");
|
|
26126
26676
|
return code;
|
|
26127
26677
|
}
|