beplus-mcp 0.30.0 → 0.32.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 +8 -1
- package/dist/index.js +458 -81
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6145,11 +6145,11 @@ var require_format = __commonJS({
|
|
|
6145
6145
|
}
|
|
6146
6146
|
function getFormat(fmtDef) {
|
|
6147
6147
|
const code = fmtDef instanceof RegExp ? (0, codegen_1.regexpCode)(fmtDef) : opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(schema)}` : void 0;
|
|
6148
|
-
const
|
|
6148
|
+
const fmt3 = gen.scopeValue("formats", { key: schema, ref: fmtDef, code });
|
|
6149
6149
|
if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) {
|
|
6150
|
-
return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${
|
|
6150
|
+
return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${fmt3}.validate`];
|
|
6151
6151
|
}
|
|
6152
|
-
return ["string", fmtDef,
|
|
6152
|
+
return ["string", fmtDef, fmt3];
|
|
6153
6153
|
}
|
|
6154
6154
|
function validCondition() {
|
|
6155
6155
|
if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) {
|
|
@@ -6811,8 +6811,8 @@ var require_limit = __commonJS({
|
|
|
6811
6811
|
ref: self.formats,
|
|
6812
6812
|
code: opts.code.formats
|
|
6813
6813
|
});
|
|
6814
|
-
const
|
|
6815
|
-
cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${
|
|
6814
|
+
const fmt3 = gen.const("fmt", (0, codegen_1._)`${fmts}[${fCxt.schemaCode}]`);
|
|
6815
|
+
cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt3} != "object"`, (0, codegen_1._)`${fmt3} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt3}.compare != "function"`, compareCode(fmt3)));
|
|
6816
6816
|
}
|
|
6817
6817
|
function validateFormat() {
|
|
6818
6818
|
const format = fCxt.schema;
|
|
@@ -6822,15 +6822,15 @@ var require_limit = __commonJS({
|
|
|
6822
6822
|
if (typeof fmtDef != "object" || fmtDef instanceof RegExp || typeof fmtDef.compare != "function") {
|
|
6823
6823
|
throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
|
|
6824
6824
|
}
|
|
6825
|
-
const
|
|
6825
|
+
const fmt3 = gen.scopeValue("formats", {
|
|
6826
6826
|
key: format,
|
|
6827
6827
|
ref: fmtDef,
|
|
6828
6828
|
code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format)}` : void 0
|
|
6829
6829
|
});
|
|
6830
|
-
cxt.fail$data(compareCode(
|
|
6830
|
+
cxt.fail$data(compareCode(fmt3));
|
|
6831
6831
|
}
|
|
6832
|
-
function compareCode(
|
|
6833
|
-
return (0, codegen_1._)`${
|
|
6832
|
+
function compareCode(fmt3) {
|
|
6833
|
+
return (0, codegen_1._)`${fmt3}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`;
|
|
6834
6834
|
}
|
|
6835
6835
|
},
|
|
6836
6836
|
dependencies: ["format"]
|
|
@@ -23015,15 +23015,15 @@ function probeJpeg(b) {
|
|
|
23015
23015
|
function probeWebp(b) {
|
|
23016
23016
|
if (b.length < 30) return null;
|
|
23017
23017
|
if (b.toString("ascii", 0, 4) !== "RIFF" || b.toString("ascii", 8, 12) !== "WEBP") return null;
|
|
23018
|
-
const
|
|
23019
|
-
if (
|
|
23018
|
+
const fmt3 = b.toString("ascii", 12, 16);
|
|
23019
|
+
if (fmt3 === "VP8 ") {
|
|
23020
23020
|
return { width: b.readUInt16LE(26) & 16383, height: b.readUInt16LE(28) & 16383 };
|
|
23021
23021
|
}
|
|
23022
|
-
if (
|
|
23022
|
+
if (fmt3 === "VP8L") {
|
|
23023
23023
|
const bits = b.readUInt32LE(21);
|
|
23024
23024
|
return { width: (bits & 16383) + 1, height: (bits >> 14 & 16383) + 1 };
|
|
23025
23025
|
}
|
|
23026
|
-
if (
|
|
23026
|
+
if (fmt3 === "VP8X") {
|
|
23027
23027
|
const w = b[24] | b[25] << 8 | b[26] << 16;
|
|
23028
23028
|
const h = b[27] | b[28] << 8 | b[29] << 16;
|
|
23029
23029
|
return { width: w + 1, height: h + 1 };
|
|
@@ -24086,6 +24086,8 @@ import { createHash } from "crypto";
|
|
|
24086
24086
|
|
|
24087
24087
|
// src/canvas/api.ts
|
|
24088
24088
|
var PINNED_CORE_VERSION = "ca7b3b406184c7f679f555c3a008013197182b23";
|
|
24089
|
+
var RUN_REQUEST_TIMEOUT_MS = 4e4;
|
|
24090
|
+
var FOCUS_REQUEST_TIMEOUT_MS = 12e3;
|
|
24089
24091
|
function errorRaw(err) {
|
|
24090
24092
|
if (!(err instanceof ApiError)) return void 0;
|
|
24091
24093
|
const raw = err.body?.error?.raw;
|
|
@@ -24126,13 +24128,13 @@ var CanvasApi = class {
|
|
|
24126
24128
|
createBoard(body2) {
|
|
24127
24129
|
return this.req("POST", "/boards", { body: body2 });
|
|
24128
24130
|
}
|
|
24129
|
-
snapshot(
|
|
24130
|
-
return this.req("GET", `/boards/${
|
|
24131
|
+
snapshot(boardId4) {
|
|
24132
|
+
return this.req("GET", `/boards/${boardId4}`);
|
|
24131
24133
|
}
|
|
24132
24134
|
/** Replay depois de `after`. `null` quando o servidor manda reler a foto (410). */
|
|
24133
|
-
async opsAfter(
|
|
24135
|
+
async opsAfter(boardId4, after) {
|
|
24134
24136
|
try {
|
|
24135
|
-
const { envelopes } = await this.req("GET", `/boards/${
|
|
24137
|
+
const { envelopes } = await this.req("GET", `/boards/${boardId4}/ops`, {
|
|
24136
24138
|
query: { after }
|
|
24137
24139
|
});
|
|
24138
24140
|
return envelopes;
|
|
@@ -24141,8 +24143,8 @@ var CanvasApi = class {
|
|
|
24141
24143
|
throw e;
|
|
24142
24144
|
}
|
|
24143
24145
|
}
|
|
24144
|
-
async commit(
|
|
24145
|
-
const send = () => this.req("POST", `/boards/${
|
|
24146
|
+
async commit(boardId4, ops) {
|
|
24147
|
+
const send = () => this.req("POST", `/boards/${boardId4}/ops`, {
|
|
24146
24148
|
body: { coreVersion: this.coreVersion, ops }
|
|
24147
24149
|
});
|
|
24148
24150
|
try {
|
|
@@ -24163,18 +24165,18 @@ var CanvasApi = class {
|
|
|
24163
24165
|
}
|
|
24164
24166
|
}
|
|
24165
24167
|
}
|
|
24166
|
-
members(
|
|
24167
|
-
return this.req("GET", `/boards/${
|
|
24168
|
+
members(boardId4) {
|
|
24169
|
+
return this.req("GET", `/boards/${boardId4}/members`);
|
|
24168
24170
|
}
|
|
24169
|
-
putMember(
|
|
24170
|
-
return this.req("PUT", `/boards/${
|
|
24171
|
+
putMember(boardId4, userId, role) {
|
|
24172
|
+
return this.req("PUT", `/boards/${boardId4}/members/${userId}`, { body: { role } });
|
|
24171
24173
|
}
|
|
24172
|
-
removeMember(
|
|
24173
|
-
return this.req("DELETE", `/boards/${
|
|
24174
|
+
removeMember(boardId4, userId) {
|
|
24175
|
+
return this.req("DELETE", `/boards/${boardId4}/members/${userId}`);
|
|
24174
24176
|
}
|
|
24175
24177
|
// ── comentários (academy-backend #313) ────────────────────────────────────
|
|
24176
|
-
listComments(
|
|
24177
|
-
return this.req("GET", `/boards/${
|
|
24178
|
+
listComments(boardId4, status = "open") {
|
|
24179
|
+
return this.req("GET", `/boards/${boardId4}/comments`, { query: { status } });
|
|
24178
24180
|
}
|
|
24179
24181
|
/**
|
|
24180
24182
|
* Escrita de comentário passa por aqui: o back limita a 60 por minuto por pessoa
|
|
@@ -24200,14 +24202,30 @@ var CanvasApi = class {
|
|
|
24200
24202
|
}
|
|
24201
24203
|
}
|
|
24202
24204
|
/** Raiz ou resposta. O back devolve sempre a THREAD DA RAIZ (resposta nova = última de `replies`). */
|
|
24203
|
-
createComment(
|
|
24204
|
-
return this.write("POST", `/boards/${
|
|
24205
|
+
createComment(boardId4, body2) {
|
|
24206
|
+
return this.write("POST", `/boards/${boardId4}/comments`, body2);
|
|
24205
24207
|
}
|
|
24206
|
-
resolveComment(
|
|
24207
|
-
return this.write("POST", `/boards/${
|
|
24208
|
+
resolveComment(boardId4, commentId2) {
|
|
24209
|
+
return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/resolve`);
|
|
24208
24210
|
}
|
|
24209
|
-
reopenComment(
|
|
24210
|
-
return this.write("POST", `/boards/${
|
|
24211
|
+
reopenComment(boardId4, commentId2) {
|
|
24212
|
+
return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/reopen`);
|
|
24213
|
+
}
|
|
24214
|
+
// ── execução pela aba do Canvas (CR1, academy-backend #318) ───────────────
|
|
24215
|
+
/**
|
|
24216
|
+
* Pede para a aba do Canvas da pessoa rodar. O servidor espera a aba até 20 s;
|
|
24217
|
+
* aqui a espera é maior para o prazo do servidor vencer antes do nosso.
|
|
24218
|
+
*/
|
|
24219
|
+
runRequest(boardId4, body2) {
|
|
24220
|
+
return this.req("POST", `/boards/${boardId4}/run-requests`, { body: body2, timeoutMs: RUN_REQUEST_TIMEOUT_MS });
|
|
24221
|
+
}
|
|
24222
|
+
runStatus(boardId4, requestId) {
|
|
24223
|
+
return this.req("GET", `/boards/${boardId4}/run-requests/${requestId}`);
|
|
24224
|
+
}
|
|
24225
|
+
// ── enquadrar a aba do Canvas (CF1) ───────────────────────────────────────
|
|
24226
|
+
/** Pede para a aba do Canvas da pessoa enquadrar nós. O servidor espera a aba até 5 s. */
|
|
24227
|
+
focusRequest(boardId4, body2) {
|
|
24228
|
+
return this.req("POST", `/boards/${boardId4}/focus-requests`, { body: body2, timeoutMs: FOCUS_REQUEST_TIMEOUT_MS });
|
|
24211
24229
|
}
|
|
24212
24230
|
/** Só para os testes conferirem o aprendizado. */
|
|
24213
24231
|
get currentCoreVersion() {
|
|
@@ -24483,8 +24501,8 @@ function checkOp(op) {
|
|
|
24483
24501
|
return null;
|
|
24484
24502
|
}
|
|
24485
24503
|
}
|
|
24486
|
-
function envelope(
|
|
24487
|
-
return { id: randomUUID(), boardId:
|
|
24504
|
+
function envelope(boardId4, op, actorId) {
|
|
24505
|
+
return { id: randomUUID(), boardId: boardId4, clientId: CLIENT_ID, actorId, seq: null, ts: Date.now(), op };
|
|
24488
24506
|
}
|
|
24489
24507
|
function checkEnvelope(env) {
|
|
24490
24508
|
const bad = checkOp(env.op);
|
|
@@ -24539,13 +24557,13 @@ function makeEdge(init) {
|
|
|
24539
24557
|
}
|
|
24540
24558
|
var setValue = (value) => ({ kind: "value", value });
|
|
24541
24559
|
var unsetValue = () => ({ kind: "unset" });
|
|
24542
|
-
function removeNodeOps(doc,
|
|
24560
|
+
function removeNodeOps(doc, nodeId3) {
|
|
24543
24561
|
const ops = [];
|
|
24544
|
-
const edgeIds = Object.keys(doc.edges).filter((id) => doc.edges[id].source ===
|
|
24562
|
+
const edgeIds = Object.keys(doc.edges).filter((id) => doc.edges[id].source === nodeId3 || doc.edges[id].target === nodeId3).sort();
|
|
24545
24563
|
for (const edgeId of edgeIds) ops.push({ type: "edge.remove", edgeId });
|
|
24546
|
-
const history = Object.hasOwn(doc.nodeHistory,
|
|
24547
|
-
if (history) for (const resultId of [...history].reverse()) ops.push({ type: "history.remove", nodeId, resultId });
|
|
24548
|
-
ops.push({ type: "node.remove", nodeId });
|
|
24564
|
+
const history = Object.hasOwn(doc.nodeHistory, nodeId3) ? doc.nodeHistory[nodeId3] : void 0;
|
|
24565
|
+
if (history) for (const resultId of [...history].reverse()) ops.push({ type: "history.remove", nodeId: nodeId3, resultId });
|
|
24566
|
+
ops.push({ type: "node.remove", nodeId: nodeId3 });
|
|
24549
24567
|
return ops;
|
|
24550
24568
|
}
|
|
24551
24569
|
function batchOf(ops) {
|
|
@@ -24561,14 +24579,14 @@ var OpTooLargeError = class extends Error {
|
|
|
24561
24579
|
op;
|
|
24562
24580
|
bytes;
|
|
24563
24581
|
};
|
|
24564
|
-
function packEnvelopes(
|
|
24582
|
+
function packEnvelopes(boardId4, ops, actorId, limit = MAX_OP_BYTES - 1024) {
|
|
24565
24583
|
const bytes = (v) => Buffer.byteLength(JSON.stringify(v));
|
|
24566
|
-
const overhead = bytes(envelope(
|
|
24584
|
+
const overhead = bytes(envelope(boardId4, { type: "batch", ops: [] }, actorId));
|
|
24567
24585
|
const out = [];
|
|
24568
24586
|
let cur = [];
|
|
24569
24587
|
let size = overhead;
|
|
24570
24588
|
const flush = () => {
|
|
24571
|
-
if (cur.length) out.push(envelope(
|
|
24589
|
+
if (cur.length) out.push(envelope(boardId4, batchOf(cur), actorId));
|
|
24572
24590
|
cur = [];
|
|
24573
24591
|
size = overhead;
|
|
24574
24592
|
};
|
|
@@ -24946,8 +24964,8 @@ function mmss(t) {
|
|
|
24946
24964
|
const pad = (n) => String(n).padStart(2, "0");
|
|
24947
24965
|
return h ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
|
|
24948
24966
|
}
|
|
24949
|
-
function nodeRef(doc,
|
|
24950
|
-
const node = doc.nodes[
|
|
24967
|
+
function nodeRef(doc, nodeId3) {
|
|
24968
|
+
const node = doc.nodes[nodeId3];
|
|
24951
24969
|
const kind = nodeKind(node) ?? node.type;
|
|
24952
24970
|
return `"${oneLine(nodeName(node), 60)}" (${kind}, ${node.id})`;
|
|
24953
24971
|
}
|
|
@@ -24971,13 +24989,13 @@ function outputKeyOf(entry) {
|
|
|
24971
24989
|
if (typeof entry.url === "string" && entry.url !== "") return `url-${hashString(entry.url)}`;
|
|
24972
24990
|
return null;
|
|
24973
24991
|
}
|
|
24974
|
-
function versionOf(doc,
|
|
24975
|
-
const ids = doc.nodeHistory?.[
|
|
24992
|
+
function versionOf(doc, nodeId3, outputKey) {
|
|
24993
|
+
const ids = doc.nodeHistory?.[nodeId3];
|
|
24976
24994
|
if (Array.isArray(ids)) {
|
|
24977
24995
|
const i = ids.indexOf(outputKey);
|
|
24978
24996
|
if (i >= 0) return i + 1;
|
|
24979
24997
|
}
|
|
24980
|
-
const history = doc.nodes[
|
|
24998
|
+
const history = doc.nodes[nodeId3]?.data.history;
|
|
24981
24999
|
if (Array.isArray(history)) {
|
|
24982
25000
|
const i = history.findIndex(
|
|
24983
25001
|
(h) => typeof h === "object" && h !== null && !Array.isArray(h) && outputKeyOf(h) === outputKey
|
|
@@ -25447,17 +25465,17 @@ function holdsOne(doc, node) {
|
|
|
25447
25465
|
if (node.data.holds === "many") return false;
|
|
25448
25466
|
return itemsOf(node).length === 1 && Object.values(doc.edges).some((e) => e.source === node.id);
|
|
25449
25467
|
}
|
|
25450
|
-
function needMediaNode(doc,
|
|
25451
|
-
const node = doc.nodes[
|
|
25452
|
-
if (!node) throw new CanvasToolError(`N\xF3 ${
|
|
25468
|
+
function needMediaNode(doc, nodeId3) {
|
|
25469
|
+
const node = doc.nodes[nodeId3];
|
|
25470
|
+
if (!node) throw new CanvasToolError(`N\xF3 ${nodeId3} n\xE3o existe no board. Releia com canvas_read_board.`);
|
|
25453
25471
|
if (nodeKind(node) !== "media") {
|
|
25454
25472
|
throw new CanvasToolError(
|
|
25455
|
-
`O n\xF3 ${
|
|
25473
|
+
`O n\xF3 ${nodeId3} ("${oneLine(nodeName(node), 40)}") \xE9 ${nodeKind(node) ?? node.type}, n\xE3o M\xEDdia. Passe o id de um n\xF3 media, ou omita node_id para criar um n\xF3 M\xEDdia novo.`
|
|
25456
25474
|
);
|
|
25457
25475
|
}
|
|
25458
25476
|
return node;
|
|
25459
25477
|
}
|
|
25460
|
-
function payload(u,
|
|
25478
|
+
function payload(u, nodeId3) {
|
|
25461
25479
|
const out = { url: u.url, mime: u.mime, bytes: u.bytes };
|
|
25462
25480
|
if (u.meta.duration !== void 0) out.duration = u.meta.duration;
|
|
25463
25481
|
if (u.meta.width !== void 0) out.width = u.meta.width;
|
|
@@ -25466,14 +25484,14 @@ function payload(u, nodeId) {
|
|
|
25466
25484
|
out.media_item = mediaItem(u);
|
|
25467
25485
|
if (u.kind !== "file") {
|
|
25468
25486
|
out.sequence_asset = sequenceAsset(u);
|
|
25469
|
-
out.libraryItemId = libraryItemId(u,
|
|
25487
|
+
out.libraryItemId = libraryItemId(u, nodeId3);
|
|
25470
25488
|
}
|
|
25471
25489
|
return JSON.stringify(out);
|
|
25472
25490
|
}
|
|
25473
|
-
function sequenceHint(u,
|
|
25491
|
+
function sequenceHint(u, nodeId3) {
|
|
25474
25492
|
if (u.kind === "file") return "Documento: entra no n\xF3 M\xEDdia, n\xE3o na timeline da Montagem.";
|
|
25475
25493
|
const lines = [
|
|
25476
|
-
"Num clipe da timeline da Montagem (data.timeline; data.sequence s\xF3 em n\xF3 que ainda n\xE3o migrou): use sequence_asset como `asset` e libraryItemId como est\xE1" + (
|
|
25494
|
+
"Num clipe da timeline da Montagem (data.timeline; data.sequence s\xF3 em n\xF3 que ainda n\xE3o migrou): use sequence_asset como `asset` e libraryItemId como est\xE1" + (nodeId3 ? "." : ", trocando <id do n\xF3 M\xEDdia> pelo id do n\xF3 onde o item ficou.")
|
|
25477
25495
|
];
|
|
25478
25496
|
if (u.meta.duration !== void 0) {
|
|
25479
25497
|
const at30 = Math.floor(u.meta.duration * 30 + 1e-9);
|
|
@@ -25530,14 +25548,14 @@ URL: ${u.url}`;
|
|
|
25530
25548
|
if (!args.board_id) {
|
|
25531
25549
|
return [head, "", payload(u), "", sequenceHint(u)].join("\n");
|
|
25532
25550
|
}
|
|
25533
|
-
const
|
|
25534
|
-
let
|
|
25551
|
+
const boardId4 = args.board_id;
|
|
25552
|
+
let nodeId3 = args.node_id;
|
|
25535
25553
|
let done;
|
|
25536
25554
|
try {
|
|
25537
|
-
done = await edit(
|
|
25555
|
+
done = await edit(boardId4, async (doc) => {
|
|
25538
25556
|
const item = mediaItem(u);
|
|
25539
|
-
if (
|
|
25540
|
-
const node = needMediaNode(doc,
|
|
25557
|
+
if (nodeId3) {
|
|
25558
|
+
const node = needMediaNode(doc, nodeId3);
|
|
25541
25559
|
const replacing = args.replace ?? holdsOne(doc, node);
|
|
25542
25560
|
const items = replacing ? [item] : [...itemsOf(node), item];
|
|
25543
25561
|
if (items.length > MAX_MEDIA_ITEMS) {
|
|
@@ -25559,7 +25577,7 @@ URL: ${u.url}`;
|
|
|
25559
25577
|
zIndex: Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1,
|
|
25560
25578
|
createdBy: await api.me()
|
|
25561
25579
|
});
|
|
25562
|
-
|
|
25580
|
+
nodeId3 = created.id;
|
|
25563
25581
|
return {
|
|
25564
25582
|
ops: [{ type: "node.add", nodeId: created.id, node: created }],
|
|
25565
25583
|
targets: [],
|
|
@@ -25575,7 +25593,363 @@ N\xE3o suba de novo: releia com canvas_read_board e grave media_item em data.ite
|
|
|
25575
25593
|
${payload(u)}`
|
|
25576
25594
|
);
|
|
25577
25595
|
}
|
|
25578
|
-
return [head, done, "", payload(u,
|
|
25596
|
+
return [head, done, "", payload(u, nodeId3), "", sequenceHint(u, nodeId3)].join("\n");
|
|
25597
|
+
})
|
|
25598
|
+
);
|
|
25599
|
+
}
|
|
25600
|
+
|
|
25601
|
+
// src/tools/canvas-run.ts
|
|
25602
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
25603
|
+
var MAX_REMEMBERED = 50;
|
|
25604
|
+
var boardId2 = external_exports.string().uuid().describe("Id (uuid) do board.");
|
|
25605
|
+
var nodeId = external_exports.string().min(1).max(128);
|
|
25606
|
+
var fmt2 = (n) => new Intl.NumberFormat("pt-BR").format(n);
|
|
25607
|
+
var dia = (n) => n === null || n === void 0 ? "sem estimativa" : `${fmt2(n)} \u{1F48E}`;
|
|
25608
|
+
function prettyClient(name) {
|
|
25609
|
+
if (!name) return "MCP";
|
|
25610
|
+
return name.split(/[-_\s]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join(" ").slice(0, 80);
|
|
25611
|
+
}
|
|
25612
|
+
function modeText(mode) {
|
|
25613
|
+
return mode === "simulated" ? "Simulado (n\xE3o gasta)" : mode === "live" ? "Real" : mode ?? "?";
|
|
25614
|
+
}
|
|
25615
|
+
function estimateLines(estimate, titleOf) {
|
|
25616
|
+
if (!estimate) return [];
|
|
25617
|
+
const lines = [];
|
|
25618
|
+
const nodes = estimate.nodes ?? [];
|
|
25619
|
+
const now = nodes.filter((n) => n.willRun);
|
|
25620
|
+
for (const n of now) {
|
|
25621
|
+
const cost = n.error ? `estimativa falhou (${n.error})` : dia(n.diamonds);
|
|
25622
|
+
lines.push(`- ${n.title ?? titleOf(n.nodeId)} (${n.nodeId}): ${cost}`);
|
|
25623
|
+
}
|
|
25624
|
+
if (estimate.omitted) {
|
|
25625
|
+
lines.push(`\u2026 e mais ${estimate.omitted} n\xF3(s) fora da lista (a resposta da aba foi cortada pelo tamanho; o total vale para todos).`);
|
|
25626
|
+
}
|
|
25627
|
+
const cached2 = estimate.cached ?? nodes.filter((n) => !n.willRun && !n.afterApproval).length;
|
|
25628
|
+
if (cached2) lines.push(`Em cache (n\xE3o rodam): ${cached2}.`);
|
|
25629
|
+
const later = estimate.counts?.later ?? estimate.later?.nodes?.length ?? 0;
|
|
25630
|
+
if (later) lines.push(`Esperando aprova\xE7\xE3o no board depois disso: ${later} n\xF3(s), \u2248 ${dia(estimate.later?.total ?? 0)} (fora desta conta).`);
|
|
25631
|
+
const unknown2 = estimate.counts?.unknown ?? estimate.unknown?.length ?? 0;
|
|
25632
|
+
if (unknown2) {
|
|
25633
|
+
lines.push(`Sem estimativa: ${unknown2} n\xF3(s) (texto cobra por token; o total \xE9 piso).`);
|
|
25634
|
+
}
|
|
25635
|
+
return lines;
|
|
25636
|
+
}
|
|
25637
|
+
function targetOf(args) {
|
|
25638
|
+
const given = [args.node_ids?.length ? 1 : 0, args.from_node_id ? 1 : 0, args.all ? 1 : 0].reduce((a, b) => a + b, 0);
|
|
25639
|
+
if (given !== 1) {
|
|
25640
|
+
throw new CanvasToolError("Diga o que rodar com UM destes: node_ids (os n\xF3s), from_node_id (daqui para frente) ou all: true (tudo).");
|
|
25641
|
+
}
|
|
25642
|
+
if (args.all) return { kind: "all" };
|
|
25643
|
+
if (args.from_node_id) return { kind: "from", nodeId: args.from_node_id };
|
|
25644
|
+
return { kind: "nodes", nodeIds: [...new Set(args.node_ids)] };
|
|
25645
|
+
}
|
|
25646
|
+
function registerCanvasRunTools(server, api, run) {
|
|
25647
|
+
const remembered = /* @__PURE__ */ new Map();
|
|
25648
|
+
const remember = (requestId, entry) => {
|
|
25649
|
+
remembered.delete(requestId);
|
|
25650
|
+
remembered.set(requestId, entry);
|
|
25651
|
+
while (remembered.size > MAX_REMEMBERED) remembered.delete(remembered.keys().next().value);
|
|
25652
|
+
};
|
|
25653
|
+
async function titles(board) {
|
|
25654
|
+
try {
|
|
25655
|
+
const snap = await api.snapshot(board);
|
|
25656
|
+
return (id) => snap.doc.nodes[id] ? nodeName(snap.doc.nodes[id]) : id;
|
|
25657
|
+
} catch {
|
|
25658
|
+
return (id) => id;
|
|
25659
|
+
}
|
|
25660
|
+
}
|
|
25661
|
+
function explain2(e, requestId) {
|
|
25662
|
+
if (!(e instanceof ApiError)) throw e;
|
|
25663
|
+
const reason = errorRaw(e)?.reason;
|
|
25664
|
+
const unknownFate = requestId && (e.httpStatus === 0 || e.httpStatus === 408 || reason === "tab-timeout" || reason === "tab-bad-answer" || e.httpStatus >= 500 && e.httpStatus !== 501);
|
|
25665
|
+
if (unknownFate) {
|
|
25666
|
+
throw new CanvasToolError(
|
|
25667
|
+
`N\xE3o d\xE1 para saber se a aba come\xE7ou a rodar (${e.message}). N\xC3O pe\xE7a um pedido novo: consulte canvas_run_status com request_id "${requestId}". Se precisar repetir, chame canvas_run com o mesmo request_id: \xE9 seguro, a aba n\xE3o roda duas vezes.`
|
|
25668
|
+
);
|
|
25669
|
+
}
|
|
25670
|
+
if (reason === "no-tab") {
|
|
25671
|
+
throw new CanvasToolError(
|
|
25672
|
+
"Nenhuma aba do Canvas sua est\xE1 aberta neste board (ou a aba \xE9 de uma vers\xE3o sem esta fun\xE7\xE3o). Pe\xE7a para abrir o board no Canvas com a mesma conta deste MCP e tente de novo. Nada rodou."
|
|
25673
|
+
);
|
|
25674
|
+
}
|
|
25675
|
+
if (e.httpStatus === 404 && reason !== "unknown-request") {
|
|
25676
|
+
if (!/board/i.test(e.message)) {
|
|
25677
|
+
throw new CanvasToolError("O servidor do Canvas ainda n\xE3o tem a execu\xE7\xE3o pelo MCP (academy-backend sem a CR1). Nada rodou.");
|
|
25678
|
+
}
|
|
25679
|
+
throw new CanvasToolError("Board n\xE3o encontrado, ou voc\xEA n\xE3o \xE9 membro dele. Confira o id em canvas_list_boards.");
|
|
25680
|
+
}
|
|
25681
|
+
if (e.httpStatus === 403 && (!api.usesPat || /ialab:/.test(e.message))) {
|
|
25682
|
+
throw new CanvasToolError(
|
|
25683
|
+
"O login pelo navegador deste MCP n\xE3o tem a permiss\xE3o ialab:spend, que rodar exige. Rode `beplus-mcp login` de novo e autorize."
|
|
25684
|
+
);
|
|
25685
|
+
}
|
|
25686
|
+
if (e.httpStatus === 403) throw new CanvasToolError(`Sem permiss\xE3o para rodar neste board (s\xF3 dono e editor rodam): ${e.message}.`);
|
|
25687
|
+
if (e.httpStatus === 400) throw new CanvasToolError(`O servidor recusou o pedido: ${e.message}.`);
|
|
25688
|
+
if (e.httpStatus === 429) throw new CanvasToolError("Limite de pedidos de execu\xE7\xE3o (20 por minuto). Espere um minuto.");
|
|
25689
|
+
throw e;
|
|
25690
|
+
}
|
|
25691
|
+
function answerText(a, requestId, max, titleOf) {
|
|
25692
|
+
const est = estimateLines(a.estimate, titleOf);
|
|
25693
|
+
const total = a.estimate ? `${dia(a.estimate.total)} (teto ${fmt2(max)})` : "";
|
|
25694
|
+
const again = a.repeated ? " (a aba j\xE1 tinha recebido este request_id; esta \xE9 a mesma resposta)" : "";
|
|
25695
|
+
if (a.status === "estimate") {
|
|
25696
|
+
return [`Estimativa, nada rodou${again}. Modo ${modeText(a.mode)}. Total agora: ${total}.`, ...est, cabeLine(a, max)].filter(Boolean).join("\n");
|
|
25697
|
+
}
|
|
25698
|
+
if (a.status === "nothing") return `Nada para rodar${again}: ${a.message ?? "o board j\xE1 est\xE1 em dia."}`;
|
|
25699
|
+
const willRun = (a.estimate?.nodes ?? []).filter((n) => n.willRun).map((n) => n.nodeId);
|
|
25700
|
+
return [
|
|
25701
|
+
`Rodando pela sua aba do Canvas${again}. Modo ${modeText(a.mode)}. Estimativa: ${total}.`,
|
|
25702
|
+
...est,
|
|
25703
|
+
`request_id: ${requestId}. Andamento: canvas_run_status (board_id, request_id). Resultado: canvas_read_board com node_ids [${willRun.slice(0, 20).join(", ")}] quando terminar.`
|
|
25704
|
+
].join("\n");
|
|
25705
|
+
}
|
|
25706
|
+
function cabeLine(a, max) {
|
|
25707
|
+
if (!a.estimate) return "";
|
|
25708
|
+
return a.estimate.total <= max ? "Cabe no teto: repita sem dry_run para rodar." : `Passa do teto (${fmt2(max)}): s\xF3 roda com max_diamonds maior, se o usu\xE1rio autorizar.`;
|
|
25709
|
+
}
|
|
25710
|
+
server.registerTool(
|
|
25711
|
+
"canvas_run",
|
|
25712
|
+
{
|
|
25713
|
+
title: "Canvas: rodar n\xF3s",
|
|
25714
|
+
description: "Roda n\xF3s do board pela aba do Canvas que o usu\xE1rio tem aberta nele (mesma conta; sem aba aberta, nada roda). GASTA DIAMANTES no modo Real. A aba estima como o bot\xE3o Rodar e s\xF3 roda se a estimativa couber em max_diamonds (obrigat\xF3rio: o teto que o usu\xE1rio autorizou; nunca invente um valor alto). dry_run: true s\xF3 estima. Alvo, um destes: node_ids (roda esses n\xF3s mesmo em cache, gerando e gastando de novo, como o Rodar do cart\xE3o), from_node_id (o n\xF3 e tudo depois dele, tamb\xE9m de novo) ou all: true (s\xF3 o que mudou, como o Rodar tudo). simulated: true s\xF3 vale com a aba no modo Simulado; sem ele, a aba precisa estar no Real. Prompt com PT editado e sem aplicar no EN: a aba recusa e diz qual (aplique no cart\xE3o). Devolve o que vai rodar, o custo e o request_id; acompanhe com canvas_run_status e leia o resultado com canvas_read_board. Se a resposta disser que n\xE3o d\xE1 para saber se come\xE7ou, consulte o andamento e, para repetir, use o MESMO request_id.",
|
|
25715
|
+
inputSchema: {
|
|
25716
|
+
board_id: boardId2,
|
|
25717
|
+
node_ids: external_exports.array(nodeId).min(1).max(100).optional().describe("Os n\xF3s a rodar (como o Rodar do cart\xE3o/sele\xE7\xE3o)."),
|
|
25718
|
+
from_node_id: nodeId.optional().describe("Rodar daqui para frente: este n\xF3 e tudo que depende dele."),
|
|
25719
|
+
all: external_exports.boolean().optional().describe("true: Rodar tudo (s\xF3 o que mudou)."),
|
|
25720
|
+
max_diamonds: external_exports.number().finite().min(0).max(1e6).describe("Teto de diamantes desta execu\xE7\xE3o. Obrigat\xF3rio."),
|
|
25721
|
+
dry_run: external_exports.boolean().optional().describe("true: s\xF3 estima, nada roda."),
|
|
25722
|
+
simulated: external_exports.boolean().optional().describe("true: o pedido \xE9 para o modo Simulado (a aba precisa estar nele)."),
|
|
25723
|
+
label: external_exports.string().trim().min(1).max(80).optional().describe('Quem pede, como a aba mostra ("Rodando a pedido de \u2026").'),
|
|
25724
|
+
request_id: external_exports.string().uuid().optional().describe("S\xF3 para repetir um pedido cujo destino ficou incerto.")
|
|
25725
|
+
},
|
|
25726
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
|
|
25727
|
+
},
|
|
25728
|
+
async (args) => run(async () => {
|
|
25729
|
+
const target = targetOf(args);
|
|
25730
|
+
const requestId = (args.request_id ?? randomUUID3()).toLowerCase();
|
|
25731
|
+
const label = args.label ?? prettyClient(server.server.getClientVersion()?.name);
|
|
25732
|
+
let answer;
|
|
25733
|
+
try {
|
|
25734
|
+
answer = await api.runRequest(args.board_id, {
|
|
25735
|
+
requestId,
|
|
25736
|
+
target,
|
|
25737
|
+
maxDiamonds: args.max_diamonds,
|
|
25738
|
+
dryRun: args.dry_run === true,
|
|
25739
|
+
simulated: args.simulated === true,
|
|
25740
|
+
label
|
|
25741
|
+
});
|
|
25742
|
+
} catch (e) {
|
|
25743
|
+
explain2(e, requestId);
|
|
25744
|
+
}
|
|
25745
|
+
const titleOf = await titles(args.board_id);
|
|
25746
|
+
if (answer.ok !== true) {
|
|
25747
|
+
const est = estimateLines(answer.estimate, titleOf);
|
|
25748
|
+
throw new CanvasToolError(
|
|
25749
|
+
[`A aba recusou, nada rodou (${answer.reason ?? "sem motivo"}): ${answer.message ?? ""}`.trim(), ...est].join("\n")
|
|
25750
|
+
);
|
|
25751
|
+
}
|
|
25752
|
+
const nodeIds = (answer.estimate?.nodes ?? []).filter((n) => n.willRun).map((n) => n.nodeId);
|
|
25753
|
+
if (answer.status === "started") {
|
|
25754
|
+
remember(requestId, {
|
|
25755
|
+
boardId: args.board_id,
|
|
25756
|
+
nodeIds: nodeIds.length ? nodeIds : target.kind === "nodes" ? target.nodeIds : target.kind === "from" ? [target.nodeId] : [],
|
|
25757
|
+
...answer.estimate?.omitted ? { omitted: answer.estimate.omitted } : {}
|
|
25758
|
+
});
|
|
25759
|
+
}
|
|
25760
|
+
return answerText(answer, requestId, args.max_diamonds, titleOf);
|
|
25761
|
+
})
|
|
25762
|
+
);
|
|
25763
|
+
server.registerTool(
|
|
25764
|
+
"canvas_run_status",
|
|
25765
|
+
{
|
|
25766
|
+
title: "Canvas: andamento da execu\xE7\xE3o",
|
|
25767
|
+
description: "Andamento de um pedido do canvas_run: estado de cada n\xF3, estimado e gasto. Pergunta \xE0 aba do Canvas que rodou; se ela fechou, l\xEA o estado de execu\xE7\xE3o do board para os n\xF3s do pedido (os que este MCP lembra, ou node_ids). S\xF3 leitura.",
|
|
25768
|
+
inputSchema: {
|
|
25769
|
+
board_id: boardId2,
|
|
25770
|
+
request_id: external_exports.string().uuid().describe("O request_id que o canvas_run devolveu."),
|
|
25771
|
+
node_ids: external_exports.array(nodeId).min(1).max(100).optional().describe("Os n\xF3s a conferir, se o MCP n\xE3o lembra do pedido.")
|
|
25772
|
+
},
|
|
25773
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
25774
|
+
},
|
|
25775
|
+
async ({ board_id, request_id, node_ids }) => run(async () => {
|
|
25776
|
+
const requestId = request_id.toLowerCase();
|
|
25777
|
+
let why = "";
|
|
25778
|
+
try {
|
|
25779
|
+
return statusText(await api.runStatus(board_id, requestId), await titles(board_id));
|
|
25780
|
+
} catch (e) {
|
|
25781
|
+
const reason = errorRaw(e)?.reason;
|
|
25782
|
+
if (!(e instanceof ApiError) || reason !== "unknown-request" && reason !== "no-tab") explain2(e);
|
|
25783
|
+
why = reason === "no-tab" ? "Nenhuma aba do Canvas sua est\xE1 aberta neste board." : "Nenhuma aba aberta conhece este pedido.";
|
|
25784
|
+
}
|
|
25785
|
+
const known = node_ids ? void 0 : remembered.get(requestId);
|
|
25786
|
+
const ids = node_ids ?? known?.nodeIds;
|
|
25787
|
+
if (!ids?.length) {
|
|
25788
|
+
return `${why} Sem a aba, o andamento sai do estado de execu\xE7\xE3o do board: chame de novo com node_ids (os n\xF3s que o pedido rodava).`;
|
|
25789
|
+
}
|
|
25790
|
+
const partial2 = known?.omitted ? ` (parcial: s\xF3 os ${ids.length} n\xF3s que a aba listou; outros ${known.omitted} ficaram fora da resposta. Para eles, chame com node_ids)` : "";
|
|
25791
|
+
return `${why} Pelo estado de execu\xE7\xE3o do board${partial2}:
|
|
25792
|
+
${fromRuns(await api.snapshot(board_id), ids)}`;
|
|
25793
|
+
})
|
|
25794
|
+
);
|
|
25795
|
+
}
|
|
25796
|
+
var STATE_TEXT = {
|
|
25797
|
+
preparing: "a aba est\xE1 estimando e conferindo",
|
|
25798
|
+
running: "rodando",
|
|
25799
|
+
done: "terminou",
|
|
25800
|
+
failed: "terminou com erro",
|
|
25801
|
+
cancelled: "cancelado",
|
|
25802
|
+
refused: "recusado",
|
|
25803
|
+
nothing: "nada para rodar",
|
|
25804
|
+
estimate: "s\xF3 estimativa"
|
|
25805
|
+
};
|
|
25806
|
+
var NODE_TEXT = {
|
|
25807
|
+
idle: "parado",
|
|
25808
|
+
waiting: "esperando o montante",
|
|
25809
|
+
queued: "na fila",
|
|
25810
|
+
running: "rodando",
|
|
25811
|
+
done: "pronto",
|
|
25812
|
+
error: "erro",
|
|
25813
|
+
cancelled: "cancelado",
|
|
25814
|
+
skipped: "pulado (o montante falhou)",
|
|
25815
|
+
blocked: "esperando aprova\xE7\xE3o",
|
|
25816
|
+
claimed: "outra pessoa est\xE1 rodando",
|
|
25817
|
+
cached: "em cache"
|
|
25818
|
+
};
|
|
25819
|
+
function statusText(a, titleOf) {
|
|
25820
|
+
const head = [`Pedido ${a.requestId}: ${STATE_TEXT[a.state] ?? a.state}.`];
|
|
25821
|
+
if (a.mode) head.push(`Modo ${modeText(a.mode)}.`);
|
|
25822
|
+
if (typeof a.estimated === "number") head.push(`Estimado ${dia(a.estimated)}.`);
|
|
25823
|
+
if (typeof a.spent === "number") head.push(`Gasto at\xE9 agora ${dia(a.spent)}.`);
|
|
25824
|
+
const lines = [head.join(" ")];
|
|
25825
|
+
if (a.message) lines.push(a.message);
|
|
25826
|
+
for (const n of a.nodes ?? []) {
|
|
25827
|
+
const extra = n.error ? `: ${n.error}` : n.message ? `: ${n.message}` : "";
|
|
25828
|
+
const outs = typeof n.outputs === "number" && n.outputs > 0 ? ` (${n.outputs} sa\xEDda(s))` : "";
|
|
25829
|
+
lines.push(`- ${n.title ?? titleOf(n.nodeId)} (${n.nodeId}): ${NODE_TEXT[n.status] ?? n.status}${outs}${extra}`);
|
|
25830
|
+
}
|
|
25831
|
+
if (a.omitted) lines.push(`\u2026 e mais ${a.omitted} n\xF3(s) fora da lista (a resposta da aba foi cortada pelo tamanho).`);
|
|
25832
|
+
if (a.state === "done" || a.state === "failed") lines.push("Leia o resultado com canvas_read_board (node_ids).");
|
|
25833
|
+
return lines.join("\n");
|
|
25834
|
+
}
|
|
25835
|
+
function fromRuns(snap, ids) {
|
|
25836
|
+
const byNode = new Map(snap.runs.map((r) => [r.nodeId, r]));
|
|
25837
|
+
return ids.map((id) => {
|
|
25838
|
+
const node = snap.doc.nodes[id];
|
|
25839
|
+
const name = node ? nodeName(node) : id;
|
|
25840
|
+
if (!node) return `- ${id}: o n\xF3 n\xE3o existe mais no board`;
|
|
25841
|
+
const r = byNode.get(id);
|
|
25842
|
+
const st = r?.state ?? null;
|
|
25843
|
+
if (r?.claimedBy) return `- ${name} (${id}): rodando agora`;
|
|
25844
|
+
if (!st?.status) return `- ${name} (${id}): sem estado de execu\xE7\xE3o no servidor (n\xE3o rodou, ou \xE9 n\xF3 que roda s\xF3 na aba)`;
|
|
25845
|
+
const outs = Object.values(st.outputs ?? {}).reduce((sum, list) => sum + (Array.isArray(list) ? list.length : 0), 0);
|
|
25846
|
+
const err = typeof st.error === "string" ? st.error : st.error?.message;
|
|
25847
|
+
return `- ${name} (${id}): ${NODE_TEXT[st.status] ?? st.status}${outs ? ` (${outs} sa\xEDda(s))` : ""}${err ? `: ${err}` : ""}`;
|
|
25848
|
+
}).join("\n");
|
|
25849
|
+
}
|
|
25850
|
+
|
|
25851
|
+
// src/tools/canvas-focus.ts
|
|
25852
|
+
var MAX_IDS = 100;
|
|
25853
|
+
var nodeId2 = external_exports.string().min(1).max(128);
|
|
25854
|
+
var fmtZoom = (z) => new Intl.NumberFormat("pt-BR", { maximumFractionDigits: 2 }).format(z);
|
|
25855
|
+
function explain(e, usesPat) {
|
|
25856
|
+
if (!(e instanceof ApiError)) throw e;
|
|
25857
|
+
const reason = errorRaw(e)?.reason;
|
|
25858
|
+
if (reason === "no-tab") {
|
|
25859
|
+
throw new CanvasToolError(
|
|
25860
|
+
"Nenhuma aba do Canvas sua est\xE1 aberta neste board (ou a aba \xE9 de uma vers\xE3o sem esta fun\xE7\xE3o). Pe\xE7a para abrir o board no Canvas com a mesma conta deste MCP (ou recarregar a aba) e tente de novo. Nada mudou."
|
|
25861
|
+
);
|
|
25862
|
+
}
|
|
25863
|
+
if (reason === "tab-timeout") {
|
|
25864
|
+
throw new CanvasToolError("A aba do Canvas n\xE3o respondeu em 5 s. Ela pode ter enquadrado; chamar de novo n\xE3o faz mal.");
|
|
25865
|
+
}
|
|
25866
|
+
if (reason === "tab-bad-answer") {
|
|
25867
|
+
throw new CanvasToolError("A aba do Canvas respondeu fora do contrato. Pe\xE7a para recarregar a aba e tente de novo.");
|
|
25868
|
+
}
|
|
25869
|
+
if (e.httpStatus === 404) {
|
|
25870
|
+
if (!/board/i.test(e.message)) {
|
|
25871
|
+
throw new CanvasToolError("O servidor do Canvas ainda n\xE3o tem o enquadramento pelo MCP (academy-backend sem a CF1). Nada mudou.");
|
|
25872
|
+
}
|
|
25873
|
+
throw new CanvasToolError("Board n\xE3o encontrado, ou voc\xEA n\xE3o \xE9 membro dele. Confira o id em canvas_list_boards.");
|
|
25874
|
+
}
|
|
25875
|
+
if (e.httpStatus === 403 && (!usesPat || /ialab:/.test(e.message))) {
|
|
25876
|
+
throw new CanvasToolError("O login pelo navegador deste MCP n\xE3o tem a permiss\xE3o que o enquadramento exige. Rode `beplus-mcp login` de novo e autorize.");
|
|
25877
|
+
}
|
|
25878
|
+
if (e.httpStatus === 400) throw new CanvasToolError(`O servidor recusou o pedido: ${e.message}.`);
|
|
25879
|
+
if (e.httpStatus === 429) throw new CanvasToolError("Limite de pedidos de enquadramento (60 por minuto). Espere um minuto.");
|
|
25880
|
+
throw e;
|
|
25881
|
+
}
|
|
25882
|
+
function focusText(a) {
|
|
25883
|
+
const tab = a.tab;
|
|
25884
|
+
const name = tab?.title ? `"${tab.title}"` : "aberta";
|
|
25885
|
+
const found = a.found ?? 0;
|
|
25886
|
+
const selected = a.selected ?? 0;
|
|
25887
|
+
const zoom = typeof a.zoom === "number" ? ` Zoom ${fmtZoom(a.zoom)}.` : "";
|
|
25888
|
+
const lines = [
|
|
25889
|
+
`Enquadrado na aba do Canvas ${name}: ${found} n\xF3(s)${selected > 0 ? `, ${selected} selecionado(s)` : ", sem mexer na sele\xE7\xE3o"}.${zoom}`
|
|
25890
|
+
];
|
|
25891
|
+
if (tab?.human !== void 0 || tab?.relayed !== void 0) {
|
|
25892
|
+
const who2 = tab.human === false ? "Aba de agente (link ?frame): nenhuma aba do usu\xE1rio estava vis\xEDvel neste board." : "Aba do usu\xE1rio (a que ele est\xE1 olhando).";
|
|
25893
|
+
const via = tab.relayed ? " Repassado pela aba que o servidor escolheu." : "";
|
|
25894
|
+
lines.push(`${who2}${via}`);
|
|
25895
|
+
}
|
|
25896
|
+
if (a.mode === "follow") {
|
|
25897
|
+
lines.push("Modo seguir: a aba acompanha essa \xE1rea e fica presa nela at\xE9 o usu\xE1rio clicar no \xD7 da p\xEDlula.");
|
|
25898
|
+
} else if (a.mode === "light") {
|
|
25899
|
+
lines.push("Uma vez s\xF3: a aba n\xE3o fica presa; o usu\xE1rio segue mexendo \xE0 vontade.");
|
|
25900
|
+
}
|
|
25901
|
+
const missing = a.missing ?? [];
|
|
25902
|
+
if (missing.length > 0) {
|
|
25903
|
+
const more = a.missingMore ? ` e mais ${a.missingMore}` : "";
|
|
25904
|
+
lines.push(`N\xE3o existem no board (ou est\xE3o arquivados): ${missing.join(", ")}${more}.`);
|
|
25905
|
+
}
|
|
25906
|
+
if (tab?.visible === false) lines.push("A aba est\xE1 escondida (atr\xE1s de outra): pe\xE7a para o usu\xE1rio traz\xEA-la para a frente.");
|
|
25907
|
+
if (tab?.montage) lines.push("O usu\xE1rio est\xE1 no editor (Montagem aberta nessa aba): o board foi enquadrado por tr\xE1s e aparece quando ele fechar a Montagem.");
|
|
25908
|
+
if (tab?.wasFramed) lines.push("Essa aba estava enquadrada em outra \xE1rea (talvez a de outro agente) e agora mostra esta.");
|
|
25909
|
+
return lines.join("\n");
|
|
25910
|
+
}
|
|
25911
|
+
function registerCanvasFocusTools(server, api, run) {
|
|
25912
|
+
server.registerTool(
|
|
25913
|
+
"canvas_focus",
|
|
25914
|
+
{
|
|
25915
|
+
title: "Canvas: enquadrar a aba do usu\xE1rio",
|
|
25916
|
+
description: 'Mostra "olha aqui" ao usu\xE1rio: leva a aba do Canvas que ele est\xE1 olhando neste board (a vis\xEDvel, que n\xE3o \xE9 aba de agente aberta por link ?frame; sem nenhuma, a de um agente) at\xE9 n\xF3s ou uma se\xE7\xE3o, sem anima\xE7\xE3o, e seleciona os n\xF3s (select: false n\xE3o mexe na sele\xE7\xE3o). Padr\xE3o: enquadra e seleciona UMA vez; a aba n\xE3o fica presa e o usu\xE1rio segue mexendo. follow: true segue a \xE1rea e prende a aba at\xE9 o \xD7 (modo ?frame: acompanha as mudan\xE7as, titulo na p\xEDlula e no nome da aba); use s\xF3 quando ele pedir para acompanhar. N\xC3O gasta e N\xC3O escreve no board. Diga node_ids e/ou section_id (at\xE9 100 ids; com os dois, a sele\xE7\xE3o fica nos node_ids). zoom: "fit" (padr\xE3o, cabe na tela) ou n\xFAmero de 0.07 a 4. Devolve qual aba enquadrou (nome, se \xE9 a do usu\xE1rio ou de agente, se veio repassada, se est\xE1 escondida, se ele est\xE1 no editor da Montagem) e os ids que n\xE3o existem. Sem aba: pe\xE7a para abrir (ou recarregar) o board no Canvas. Chamar de novo com a mesma \xE1rea \xE9 seguro.',
|
|
25917
|
+
inputSchema: {
|
|
25918
|
+
board_id: external_exports.string().uuid().describe("Id (uuid) do board."),
|
|
25919
|
+
node_ids: external_exports.array(nodeId2).min(1).max(MAX_IDS).optional().describe("Os n\xF3s a mostrar (e selecionar)."),
|
|
25920
|
+
section_id: nodeId2.optional().describe("Uma se\xE7\xE3o: enquadra a se\xE7\xE3o e o que est\xE1 dentro dela."),
|
|
25921
|
+
titulo: external_exports.string().trim().min(1).max(80).optional().describe('Com follow: texto da p\xEDlula e do nome da aba ("<titulo> \xB7 <board>"). Sem follow n\xE3o aparece.'),
|
|
25922
|
+
zoom: external_exports.union([external_exports.literal("fit"), external_exports.number().finite().min(0.07).max(4)]).optional().describe('"fit" (padr\xE3o): a \xE1rea cabe na tela, zoom at\xE9 100%. N\xFAmero: zoom fixo, com a \xE1rea no meio.'),
|
|
25923
|
+
select: external_exports.boolean().optional().describe("false: n\xE3o mexe na sele\xE7\xE3o. Padr\xE3o: seleciona os n\xF3s."),
|
|
25924
|
+
follow: external_exports.boolean().optional().describe("true: segue a \xE1rea e prende a aba at\xE9 o \xD7 da p\xEDlula. Padr\xE3o false: enquadra e seleciona uma vez, sem prender.")
|
|
25925
|
+
},
|
|
25926
|
+
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: true, openWorldHint: true }
|
|
25927
|
+
},
|
|
25928
|
+
async (args) => run(async () => {
|
|
25929
|
+
const nodeIds = args.node_ids ? [...new Set(args.node_ids)] : [];
|
|
25930
|
+
if (nodeIds.length === 0 && !args.section_id) {
|
|
25931
|
+
throw new CanvasToolError("Diga o que mostrar: node_ids (os n\xF3s), section_id (uma se\xE7\xE3o) ou os dois.");
|
|
25932
|
+
}
|
|
25933
|
+
if (nodeIds.length + (args.section_id ? 1 : 0) > MAX_IDS) {
|
|
25934
|
+
throw new CanvasToolError(`At\xE9 ${MAX_IDS} ids por pedido (node_ids mais a se\xE7\xE3o).`);
|
|
25935
|
+
}
|
|
25936
|
+
let answer;
|
|
25937
|
+
try {
|
|
25938
|
+
answer = await api.focusRequest(args.board_id, {
|
|
25939
|
+
...nodeIds.length ? { nodeIds } : {},
|
|
25940
|
+
...args.section_id ? { sectionId: args.section_id } : {},
|
|
25941
|
+
...args.titulo ? { title: args.titulo } : {},
|
|
25942
|
+
...args.zoom !== void 0 ? { zoom: args.zoom } : {},
|
|
25943
|
+
...args.select !== void 0 ? { select: args.select } : {},
|
|
25944
|
+
...args.follow !== void 0 ? { follow: args.follow } : {}
|
|
25945
|
+
});
|
|
25946
|
+
} catch (e) {
|
|
25947
|
+
explain(e, api.usesPat);
|
|
25948
|
+
}
|
|
25949
|
+
if (answer.ok !== true) {
|
|
25950
|
+
throw new CanvasToolError(`A aba recusou, nada mudou (${answer.reason ?? "sem motivo"}): ${answer.message ?? ""}`.trim());
|
|
25951
|
+
}
|
|
25952
|
+
return focusText(answer);
|
|
25579
25953
|
})
|
|
25580
25954
|
);
|
|
25581
25955
|
}
|
|
@@ -25737,7 +26111,7 @@ function legacySequenceGuard(node, key) {
|
|
|
25737
26111
|
}
|
|
25738
26112
|
var SECTION_Z = -1e3;
|
|
25739
26113
|
var GAP = 80;
|
|
25740
|
-
var
|
|
26114
|
+
var boardId3 = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
|
|
25741
26115
|
var nodeIdArg2 = external_exports.string().min(1).max(128);
|
|
25742
26116
|
var point = external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() });
|
|
25743
26117
|
var LABEL_TEXT_SIZES = ["titulo", "subtitulo", "corpo"];
|
|
@@ -25870,9 +26244,9 @@ ${lines.join("\n")}
|
|
|
25870
26244
|
return `\u2705 ${done} em ${envs.length} envelope(s) (seq at\xE9 ${last}).
|
|
25871
26245
|
${lines.join("\n")}${note}`;
|
|
25872
26246
|
}
|
|
25873
|
-
function needNode(doc,
|
|
25874
|
-
const node = doc.nodes[
|
|
25875
|
-
if (!node) throw new CanvasToolError(`N\xF3 ${
|
|
26247
|
+
function needNode(doc, nodeId3) {
|
|
26248
|
+
const node = doc.nodes[nodeId3];
|
|
26249
|
+
if (!node) throw new CanvasToolError(`N\xF3 ${nodeId3} n\xE3o existe no board. Releia com canvas_read_board.`);
|
|
25876
26250
|
return node;
|
|
25877
26251
|
}
|
|
25878
26252
|
function portWarning(doc, source, sourcePort, target, targetPort) {
|
|
@@ -25987,7 +26361,7 @@ ${lines.join("\n")}${note}`;
|
|
|
25987
26361
|
title: "Canvas: ler board",
|
|
25988
26362
|
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.',
|
|
25989
26363
|
inputSchema: {
|
|
25990
|
-
board_id:
|
|
26364
|
+
board_id: boardId3,
|
|
25991
26365
|
node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados).")
|
|
25992
26366
|
},
|
|
25993
26367
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
@@ -26009,7 +26383,7 @@ ${lines.join("\n")}${note}`;
|
|
|
26009
26383
|
title: "Canvas: ler n\xF3 inteiro",
|
|
26010
26384
|
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,
|
|
26011
26385
|
inputSchema: {
|
|
26012
|
-
board_id:
|
|
26386
|
+
board_id: boardId3,
|
|
26013
26387
|
node_id: nodeIdArg2,
|
|
26014
26388
|
path: external_exports.string().trim().max(200).optional().describe('Caminho dentro do data, com pontos (ex.: "timeline.meta", "items.0"). "data" = o data todo.'),
|
|
26015
26389
|
offset: external_exports.number().int().min(0).optional().describe("In\xEDcio da parte, em caracteres (padr\xE3o 0)."),
|
|
@@ -26118,7 +26492,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26118
26492
|
title: "Canvas: adicionar n\xF3",
|
|
26119
26493
|
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,
|
|
26120
26494
|
inputSchema: {
|
|
26121
|
-
board_id:
|
|
26495
|
+
board_id: boardId3,
|
|
26122
26496
|
kind: external_exports.string().min(1).max(40),
|
|
26123
26497
|
label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
|
|
26124
26498
|
data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
|
|
@@ -26175,7 +26549,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26175
26549
|
title: "Canvas: editar n\xF3",
|
|
26176
26550
|
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,
|
|
26177
26551
|
inputSchema: {
|
|
26178
|
-
board_id:
|
|
26552
|
+
board_id: boardId3,
|
|
26179
26553
|
node_id: nodeIdArg2,
|
|
26180
26554
|
data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave."),
|
|
26181
26555
|
label: external_exports.string().trim().min(1).max(120).optional(),
|
|
@@ -26203,7 +26577,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26203
26577
|
title: "Canvas: editar v\xE1rios n\xF3s",
|
|
26204
26578
|
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.',
|
|
26205
26579
|
inputSchema: {
|
|
26206
|
-
board_id:
|
|
26580
|
+
board_id: boardId3,
|
|
26207
26581
|
nodes: external_exports.array(
|
|
26208
26582
|
external_exports.object({
|
|
26209
26583
|
node_id: nodeIdArg2,
|
|
@@ -26255,7 +26629,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26255
26629
|
title: "Canvas: gravar por caminho",
|
|
26256
26630
|
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,
|
|
26257
26631
|
inputSchema: {
|
|
26258
|
-
board_id:
|
|
26632
|
+
board_id: boardId3,
|
|
26259
26633
|
node_id: nodeIdArg2,
|
|
26260
26634
|
sets: external_exports.array(
|
|
26261
26635
|
external_exports.object({
|
|
@@ -26354,7 +26728,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26354
26728
|
title: "Canvas: ligar portas",
|
|
26355
26729
|
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.`,
|
|
26356
26730
|
inputSchema: {
|
|
26357
|
-
board_id:
|
|
26731
|
+
board_id: boardId3,
|
|
26358
26732
|
source: nodeIdArg2,
|
|
26359
26733
|
source_port: external_exports.string().min(1).max(80),
|
|
26360
26734
|
target: nodeIdArg2,
|
|
@@ -26381,7 +26755,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26381
26755
|
title: "Canvas: apagar n\xF3s/liga\xE7\xF5es",
|
|
26382
26756
|
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.`,
|
|
26383
26757
|
inputSchema: {
|
|
26384
|
-
board_id:
|
|
26758
|
+
board_id: boardId3,
|
|
26385
26759
|
node_ids: external_exports.array(nodeIdArg2).max(200).optional(),
|
|
26386
26760
|
edge_ids: external_exports.array(nodeIdArg2).max(200).optional()
|
|
26387
26761
|
},
|
|
@@ -26425,7 +26799,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26425
26799
|
{
|
|
26426
26800
|
title: "Canvas: renomear board",
|
|
26427
26801
|
description: `Troca o t\xEDtulo do board (board.set). ${READ}Precisa de papel de editor ou dono.`,
|
|
26428
|
-
inputSchema: { board_id:
|
|
26802
|
+
inputSchema: { board_id: boardId3, title: external_exports.string().trim().min(1).max(200) },
|
|
26429
26803
|
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
|
|
26430
26804
|
},
|
|
26431
26805
|
async ({ board_id, title }) => run(
|
|
@@ -26441,7 +26815,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26441
26815
|
{
|
|
26442
26816
|
title: "Canvas: membros do board",
|
|
26443
26817
|
description: "Lista quem tem acesso ao board (userId, nome, papel). S\xF3 leitura.",
|
|
26444
|
-
inputSchema: { board_id:
|
|
26818
|
+
inputSchema: { board_id: boardId3 },
|
|
26445
26819
|
annotations: { readOnlyHint: true, openWorldHint: false }
|
|
26446
26820
|
},
|
|
26447
26821
|
async ({ board_id }) => run(async () => {
|
|
@@ -26458,7 +26832,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26458
26832
|
title: "Canvas: adicionar membro",
|
|
26459
26833
|
description: "D\xE1 acesso ao board (ou troca o papel) para um usu\xE1rio pelo userId (uuid). S\xF3 o dono pode. Ainda n\xE3o h\xE1 busca por e-mail: pe\xE7a o userId ou copie de canvas_list_members de outro board.",
|
|
26460
26834
|
inputSchema: {
|
|
26461
|
-
board_id:
|
|
26835
|
+
board_id: boardId3,
|
|
26462
26836
|
user_id: external_exports.string().uuid(),
|
|
26463
26837
|
role: external_exports.enum(["editor", "viewer"]).describe("editor edita; viewer s\xF3 v\xEA.")
|
|
26464
26838
|
},
|
|
@@ -26474,7 +26848,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26474
26848
|
{
|
|
26475
26849
|
title: "Canvas: remover membro",
|
|
26476
26850
|
description: "Tira o acesso de algu\xE9m ao board (o dono tira qualquer um; quem n\xE3o \xE9 dono s\xF3 sai sozinho). O dono n\xE3o sai.",
|
|
26477
|
-
inputSchema: { board_id:
|
|
26851
|
+
inputSchema: { board_id: boardId3, user_id: external_exports.string().uuid() },
|
|
26478
26852
|
annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
|
|
26479
26853
|
},
|
|
26480
26854
|
async ({ board_id, user_id }) => run(async () => {
|
|
@@ -26485,13 +26859,15 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
26485
26859
|
registerCanvasCommentTools(server, api, run);
|
|
26486
26860
|
registerCanvasMediaTools(server, { api, cfg, run, edit, seen, placeFor });
|
|
26487
26861
|
registerCanvasLayoutTools(server, { api, run, edit });
|
|
26862
|
+
registerCanvasRunTools(server, api, run);
|
|
26863
|
+
registerCanvasFocusTools(server, api, run);
|
|
26488
26864
|
}
|
|
26489
26865
|
|
|
26490
26866
|
// src/version.ts
|
|
26491
26867
|
import { createRequire } from "module";
|
|
26492
26868
|
var require2 = createRequire(import.meta.url);
|
|
26493
26869
|
function readVersion() {
|
|
26494
|
-
if ("0.
|
|
26870
|
+
if ("0.32.0") return "0.32.0";
|
|
26495
26871
|
try {
|
|
26496
26872
|
const pkg = require2("../package.json");
|
|
26497
26873
|
return pkg.version ?? "0.0.0-unknown";
|
|
@@ -26506,7 +26882,8 @@ var SERVER_INSTRUCTIONS = [
|
|
|
26506
26882
|
"BePlus: tudo roda pela conta do usu\xE1rio (diamantes, limites e hist\xF3rico na plataforma).",
|
|
26507
26883
|
'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.',
|
|
26508
26884
|
"Calls: search_calls acha, get_call traz a transcri\xE7\xE3o.",
|
|
26509
|
-
"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
|
|
26885
|
+
"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 canvas_* n\xE3o geram m\xEDdia, exceto canvas_run: roda n\xF3s pela aba do Canvas aberta do usu\xE1rio e gasta diamantes; max_diamonds \xE9 o teto que o usu\xE1rio autorizou (dry_run s\xF3 estima). D\xEA nome (label) aos n\xF3s, n\xE3o invente tipos nem portas, e confirme antes de canvas_delete.",
|
|
26886
|
+
'Mostrar algo ao usu\xE1rio ("olha aqui"): canvas_focus leva a aba do Canvas que ele est\xE1 olhando at\xE9 os n\xF3s e os seleciona uma vez, sem prender a aba, sem gasto e sem escrita no board. follow: true prende a aba na \xE1rea at\xE9 o \xD7: s\xF3 se ele pedir.',
|
|
26510
26887
|
'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.',
|
|
26511
26888
|
'"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.'
|
|
26512
26889
|
].join("\n");
|