beplus-mcp 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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 fmt2 = gen.scopeValue("formats", { key: schema, ref: fmtDef, code });
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._)`${fmt2}.validate`];
6150
+ return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${fmt3}.validate`];
6151
6151
  }
6152
- return ["string", fmtDef, fmt2];
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 fmt2 = gen.const("fmt", (0, codegen_1._)`${fmts}[${fCxt.schemaCode}]`);
6815
- cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt2} != "object"`, (0, codegen_1._)`${fmt2} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt2}.compare != "function"`, compareCode(fmt2)));
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 fmt2 = gen.scopeValue("formats", {
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(fmt2));
6830
+ cxt.fail$data(compareCode(fmt3));
6831
6831
  }
6832
- function compareCode(fmt2) {
6833
- return (0, codegen_1._)`${fmt2}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`;
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 fmt2 = b.toString("ascii", 12, 16);
23019
- if (fmt2 === "VP8 ") {
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 (fmt2 === "VP8L") {
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 (fmt2 === "VP8X") {
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,7 @@ 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;
24089
24090
  function errorRaw(err) {
24090
24091
  if (!(err instanceof ApiError)) return void 0;
24091
24092
  const raw = err.body?.error?.raw;
@@ -24126,13 +24127,13 @@ var CanvasApi = class {
24126
24127
  createBoard(body2) {
24127
24128
  return this.req("POST", "/boards", { body: body2 });
24128
24129
  }
24129
- snapshot(boardId3) {
24130
- return this.req("GET", `/boards/${boardId3}`);
24130
+ snapshot(boardId4) {
24131
+ return this.req("GET", `/boards/${boardId4}`);
24131
24132
  }
24132
24133
  /** Replay depois de `after`. `null` quando o servidor manda reler a foto (410). */
24133
- async opsAfter(boardId3, after) {
24134
+ async opsAfter(boardId4, after) {
24134
24135
  try {
24135
- const { envelopes } = await this.req("GET", `/boards/${boardId3}/ops`, {
24136
+ const { envelopes } = await this.req("GET", `/boards/${boardId4}/ops`, {
24136
24137
  query: { after }
24137
24138
  });
24138
24139
  return envelopes;
@@ -24141,8 +24142,8 @@ var CanvasApi = class {
24141
24142
  throw e;
24142
24143
  }
24143
24144
  }
24144
- async commit(boardId3, ops) {
24145
- const send = () => this.req("POST", `/boards/${boardId3}/ops`, {
24145
+ async commit(boardId4, ops) {
24146
+ const send = () => this.req("POST", `/boards/${boardId4}/ops`, {
24146
24147
  body: { coreVersion: this.coreVersion, ops }
24147
24148
  });
24148
24149
  try {
@@ -24163,18 +24164,18 @@ var CanvasApi = class {
24163
24164
  }
24164
24165
  }
24165
24166
  }
24166
- members(boardId3) {
24167
- return this.req("GET", `/boards/${boardId3}/members`);
24167
+ members(boardId4) {
24168
+ return this.req("GET", `/boards/${boardId4}/members`);
24168
24169
  }
24169
- putMember(boardId3, userId, role) {
24170
- return this.req("PUT", `/boards/${boardId3}/members/${userId}`, { body: { role } });
24170
+ putMember(boardId4, userId, role) {
24171
+ return this.req("PUT", `/boards/${boardId4}/members/${userId}`, { body: { role } });
24171
24172
  }
24172
- removeMember(boardId3, userId) {
24173
- return this.req("DELETE", `/boards/${boardId3}/members/${userId}`);
24173
+ removeMember(boardId4, userId) {
24174
+ return this.req("DELETE", `/boards/${boardId4}/members/${userId}`);
24174
24175
  }
24175
24176
  // ── comentários (academy-backend #313) ────────────────────────────────────
24176
- listComments(boardId3, status = "open") {
24177
- return this.req("GET", `/boards/${boardId3}/comments`, { query: { status } });
24177
+ listComments(boardId4, status = "open") {
24178
+ return this.req("GET", `/boards/${boardId4}/comments`, { query: { status } });
24178
24179
  }
24179
24180
  /**
24180
24181
  * Escrita de comentário passa por aqui: o back limita a 60 por minuto por pessoa
@@ -24200,14 +24201,25 @@ var CanvasApi = class {
24200
24201
  }
24201
24202
  }
24202
24203
  /** Raiz ou resposta. O back devolve sempre a THREAD DA RAIZ (resposta nova = última de `replies`). */
24203
- createComment(boardId3, body2) {
24204
- return this.write("POST", `/boards/${boardId3}/comments`, body2);
24204
+ createComment(boardId4, body2) {
24205
+ return this.write("POST", `/boards/${boardId4}/comments`, body2);
24205
24206
  }
24206
- resolveComment(boardId3, commentId2) {
24207
- return this.write("POST", `/boards/${boardId3}/comments/${commentId2}/resolve`);
24207
+ resolveComment(boardId4, commentId2) {
24208
+ return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/resolve`);
24208
24209
  }
24209
- reopenComment(boardId3, commentId2) {
24210
- return this.write("POST", `/boards/${boardId3}/comments/${commentId2}/reopen`);
24210
+ reopenComment(boardId4, commentId2) {
24211
+ return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/reopen`);
24212
+ }
24213
+ // ── execução pela aba do Canvas (CR1, academy-backend #318) ───────────────
24214
+ /**
24215
+ * Pede para a aba do Canvas da pessoa rodar. O servidor espera a aba até 20 s;
24216
+ * aqui a espera é maior para o prazo do servidor vencer antes do nosso.
24217
+ */
24218
+ runRequest(boardId4, body2) {
24219
+ return this.req("POST", `/boards/${boardId4}/run-requests`, { body: body2, timeoutMs: RUN_REQUEST_TIMEOUT_MS });
24220
+ }
24221
+ runStatus(boardId4, requestId) {
24222
+ return this.req("GET", `/boards/${boardId4}/run-requests/${requestId}`);
24211
24223
  }
24212
24224
  /** Só para os testes conferirem o aprendizado. */
24213
24225
  get currentCoreVersion() {
@@ -24483,8 +24495,8 @@ function checkOp(op) {
24483
24495
  return null;
24484
24496
  }
24485
24497
  }
24486
- function envelope(boardId3, op, actorId) {
24487
- return { id: randomUUID(), boardId: boardId3, clientId: CLIENT_ID, actorId, seq: null, ts: Date.now(), op };
24498
+ function envelope(boardId4, op, actorId) {
24499
+ return { id: randomUUID(), boardId: boardId4, clientId: CLIENT_ID, actorId, seq: null, ts: Date.now(), op };
24488
24500
  }
24489
24501
  function checkEnvelope(env) {
24490
24502
  const bad = checkOp(env.op);
@@ -24539,13 +24551,13 @@ function makeEdge(init) {
24539
24551
  }
24540
24552
  var setValue = (value) => ({ kind: "value", value });
24541
24553
  var unsetValue = () => ({ kind: "unset" });
24542
- function removeNodeOps(doc, nodeId) {
24554
+ function removeNodeOps(doc, nodeId2) {
24543
24555
  const ops = [];
24544
- const edgeIds = Object.keys(doc.edges).filter((id) => doc.edges[id].source === nodeId || doc.edges[id].target === nodeId).sort();
24556
+ const edgeIds = Object.keys(doc.edges).filter((id) => doc.edges[id].source === nodeId2 || doc.edges[id].target === nodeId2).sort();
24545
24557
  for (const edgeId of edgeIds) ops.push({ type: "edge.remove", edgeId });
24546
- const history = Object.hasOwn(doc.nodeHistory, nodeId) ? doc.nodeHistory[nodeId] : void 0;
24547
- if (history) for (const resultId of [...history].reverse()) ops.push({ type: "history.remove", nodeId, resultId });
24548
- ops.push({ type: "node.remove", nodeId });
24558
+ const history = Object.hasOwn(doc.nodeHistory, nodeId2) ? doc.nodeHistory[nodeId2] : void 0;
24559
+ if (history) for (const resultId of [...history].reverse()) ops.push({ type: "history.remove", nodeId: nodeId2, resultId });
24560
+ ops.push({ type: "node.remove", nodeId: nodeId2 });
24549
24561
  return ops;
24550
24562
  }
24551
24563
  function batchOf(ops) {
@@ -24561,14 +24573,14 @@ var OpTooLargeError = class extends Error {
24561
24573
  op;
24562
24574
  bytes;
24563
24575
  };
24564
- function packEnvelopes(boardId3, ops, actorId, limit = MAX_OP_BYTES - 1024) {
24576
+ function packEnvelopes(boardId4, ops, actorId, limit = MAX_OP_BYTES - 1024) {
24565
24577
  const bytes = (v) => Buffer.byteLength(JSON.stringify(v));
24566
- const overhead = bytes(envelope(boardId3, { type: "batch", ops: [] }, actorId));
24578
+ const overhead = bytes(envelope(boardId4, { type: "batch", ops: [] }, actorId));
24567
24579
  const out = [];
24568
24580
  let cur = [];
24569
24581
  let size = overhead;
24570
24582
  const flush = () => {
24571
- if (cur.length) out.push(envelope(boardId3, batchOf(cur), actorId));
24583
+ if (cur.length) out.push(envelope(boardId4, batchOf(cur), actorId));
24572
24584
  cur = [];
24573
24585
  size = overhead;
24574
24586
  };
@@ -24946,8 +24958,8 @@ function mmss(t) {
24946
24958
  const pad = (n) => String(n).padStart(2, "0");
24947
24959
  return h ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
24948
24960
  }
24949
- function nodeRef(doc, nodeId) {
24950
- const node = doc.nodes[nodeId];
24961
+ function nodeRef(doc, nodeId2) {
24962
+ const node = doc.nodes[nodeId2];
24951
24963
  const kind = nodeKind(node) ?? node.type;
24952
24964
  return `"${oneLine(nodeName(node), 60)}" (${kind}, ${node.id})`;
24953
24965
  }
@@ -24971,13 +24983,13 @@ function outputKeyOf(entry) {
24971
24983
  if (typeof entry.url === "string" && entry.url !== "") return `url-${hashString(entry.url)}`;
24972
24984
  return null;
24973
24985
  }
24974
- function versionOf(doc, nodeId, outputKey) {
24975
- const ids = doc.nodeHistory?.[nodeId];
24986
+ function versionOf(doc, nodeId2, outputKey) {
24987
+ const ids = doc.nodeHistory?.[nodeId2];
24976
24988
  if (Array.isArray(ids)) {
24977
24989
  const i = ids.indexOf(outputKey);
24978
24990
  if (i >= 0) return i + 1;
24979
24991
  }
24980
- const history = doc.nodes[nodeId]?.data.history;
24992
+ const history = doc.nodes[nodeId2]?.data.history;
24981
24993
  if (Array.isArray(history)) {
24982
24994
  const i = history.findIndex(
24983
24995
  (h) => typeof h === "object" && h !== null && !Array.isArray(h) && outputKeyOf(h) === outputKey
@@ -25447,17 +25459,17 @@ function holdsOne(doc, node) {
25447
25459
  if (node.data.holds === "many") return false;
25448
25460
  return itemsOf(node).length === 1 && Object.values(doc.edges).some((e) => e.source === node.id);
25449
25461
  }
25450
- function needMediaNode(doc, nodeId) {
25451
- const node = doc.nodes[nodeId];
25452
- if (!node) throw new CanvasToolError(`N\xF3 ${nodeId} n\xE3o existe no board. Releia com canvas_read_board.`);
25462
+ function needMediaNode(doc, nodeId2) {
25463
+ const node = doc.nodes[nodeId2];
25464
+ if (!node) throw new CanvasToolError(`N\xF3 ${nodeId2} n\xE3o existe no board. Releia com canvas_read_board.`);
25453
25465
  if (nodeKind(node) !== "media") {
25454
25466
  throw new CanvasToolError(
25455
- `O n\xF3 ${nodeId} ("${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.`
25467
+ `O n\xF3 ${nodeId2} ("${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
25468
  );
25457
25469
  }
25458
25470
  return node;
25459
25471
  }
25460
- function payload(u, nodeId) {
25472
+ function payload(u, nodeId2) {
25461
25473
  const out = { url: u.url, mime: u.mime, bytes: u.bytes };
25462
25474
  if (u.meta.duration !== void 0) out.duration = u.meta.duration;
25463
25475
  if (u.meta.width !== void 0) out.width = u.meta.width;
@@ -25466,14 +25478,14 @@ function payload(u, nodeId) {
25466
25478
  out.media_item = mediaItem(u);
25467
25479
  if (u.kind !== "file") {
25468
25480
  out.sequence_asset = sequenceAsset(u);
25469
- out.libraryItemId = libraryItemId(u, nodeId);
25481
+ out.libraryItemId = libraryItemId(u, nodeId2);
25470
25482
  }
25471
25483
  return JSON.stringify(out);
25472
25484
  }
25473
- function sequenceHint(u, nodeId) {
25485
+ function sequenceHint(u, nodeId2) {
25474
25486
  if (u.kind === "file") return "Documento: entra no n\xF3 M\xEDdia, n\xE3o na timeline da Montagem.";
25475
25487
  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" + (nodeId ? "." : ", trocando <id do n\xF3 M\xEDdia> pelo id do n\xF3 onde o item ficou.")
25488
+ "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" + (nodeId2 ? "." : ", trocando <id do n\xF3 M\xEDdia> pelo id do n\xF3 onde o item ficou.")
25477
25489
  ];
25478
25490
  if (u.meta.duration !== void 0) {
25479
25491
  const at30 = Math.floor(u.meta.duration * 30 + 1e-9);
@@ -25530,14 +25542,14 @@ URL: ${u.url}`;
25530
25542
  if (!args.board_id) {
25531
25543
  return [head, "", payload(u), "", sequenceHint(u)].join("\n");
25532
25544
  }
25533
- const boardId3 = args.board_id;
25534
- let nodeId = args.node_id;
25545
+ const boardId4 = args.board_id;
25546
+ let nodeId2 = args.node_id;
25535
25547
  let done;
25536
25548
  try {
25537
- done = await edit(boardId3, async (doc) => {
25549
+ done = await edit(boardId4, async (doc) => {
25538
25550
  const item = mediaItem(u);
25539
- if (nodeId) {
25540
- const node = needMediaNode(doc, nodeId);
25551
+ if (nodeId2) {
25552
+ const node = needMediaNode(doc, nodeId2);
25541
25553
  const replacing = args.replace ?? holdsOne(doc, node);
25542
25554
  const items = replacing ? [item] : [...itemsOf(node), item];
25543
25555
  if (items.length > MAX_MEDIA_ITEMS) {
@@ -25559,7 +25571,7 @@ URL: ${u.url}`;
25559
25571
  zIndex: Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1,
25560
25572
  createdBy: await api.me()
25561
25573
  });
25562
- nodeId = created.id;
25574
+ nodeId2 = created.id;
25563
25575
  return {
25564
25576
  ops: [{ type: "node.add", nodeId: created.id, node: created }],
25565
25577
  targets: [],
@@ -25575,11 +25587,253 @@ N\xE3o suba de novo: releia com canvas_read_board e grave media_item em data.ite
25575
25587
  ${payload(u)}`
25576
25588
  );
25577
25589
  }
25578
- return [head, done, "", payload(u, nodeId), "", sequenceHint(u, nodeId)].join("\n");
25590
+ return [head, done, "", payload(u, nodeId2), "", sequenceHint(u, nodeId2)].join("\n");
25579
25591
  })
25580
25592
  );
25581
25593
  }
25582
25594
 
25595
+ // src/tools/canvas-run.ts
25596
+ import { randomUUID as randomUUID3 } from "crypto";
25597
+ var MAX_REMEMBERED = 50;
25598
+ var boardId2 = external_exports.string().uuid().describe("Id (uuid) do board.");
25599
+ var nodeId = external_exports.string().min(1).max(128);
25600
+ var fmt2 = (n) => new Intl.NumberFormat("pt-BR").format(n);
25601
+ var dia = (n) => n === null || n === void 0 ? "sem estimativa" : `${fmt2(n)} \u{1F48E}`;
25602
+ function prettyClient(name) {
25603
+ if (!name) return "MCP";
25604
+ return name.split(/[-_\s]+/).filter(Boolean).map((w) => w[0].toUpperCase() + w.slice(1)).join(" ").slice(0, 80);
25605
+ }
25606
+ function modeText(mode) {
25607
+ return mode === "simulated" ? "Simulado (n\xE3o gasta)" : mode === "live" ? "Real" : mode ?? "?";
25608
+ }
25609
+ function estimateLines(estimate, titleOf) {
25610
+ if (!estimate) return [];
25611
+ const lines = [];
25612
+ const nodes = estimate.nodes ?? [];
25613
+ const now = nodes.filter((n) => n.willRun);
25614
+ for (const n of now) {
25615
+ const cost = n.error ? `estimativa falhou (${n.error})` : dia(n.diamonds);
25616
+ lines.push(`- ${n.title ?? titleOf(n.nodeId)} (${n.nodeId}): ${cost}`);
25617
+ }
25618
+ const cached2 = nodes.filter((n) => !n.willRun && !n.afterApproval).length;
25619
+ if (cached2) lines.push(`Em cache (n\xE3o rodam): ${cached2}.`);
25620
+ const later = estimate.later?.nodes?.length ?? 0;
25621
+ 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).`);
25622
+ if (estimate.unknown?.length) {
25623
+ lines.push(`Sem estimativa: ${estimate.unknown.length} n\xF3(s) (texto cobra por token; o total \xE9 piso).`);
25624
+ }
25625
+ return lines;
25626
+ }
25627
+ function targetOf(args) {
25628
+ const given = [args.node_ids?.length ? 1 : 0, args.from_node_id ? 1 : 0, args.all ? 1 : 0].reduce((a, b) => a + b, 0);
25629
+ if (given !== 1) {
25630
+ 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).");
25631
+ }
25632
+ if (args.all) return { kind: "all" };
25633
+ if (args.from_node_id) return { kind: "from", nodeId: args.from_node_id };
25634
+ return { kind: "nodes", nodeIds: [...new Set(args.node_ids)] };
25635
+ }
25636
+ function registerCanvasRunTools(server, api, run) {
25637
+ const remembered = /* @__PURE__ */ new Map();
25638
+ const remember = (requestId, entry) => {
25639
+ remembered.delete(requestId);
25640
+ remembered.set(requestId, entry);
25641
+ while (remembered.size > MAX_REMEMBERED) remembered.delete(remembered.keys().next().value);
25642
+ };
25643
+ async function titles(board) {
25644
+ try {
25645
+ const snap = await api.snapshot(board);
25646
+ return (id) => snap.doc.nodes[id] ? nodeName(snap.doc.nodes[id]) : id;
25647
+ } catch {
25648
+ return (id) => id;
25649
+ }
25650
+ }
25651
+ function explain(e, requestId) {
25652
+ if (!(e instanceof ApiError)) throw e;
25653
+ const reason = errorRaw(e)?.reason;
25654
+ const unknownFate = requestId && (e.httpStatus === 0 || e.httpStatus === 408 || reason === "tab-timeout" || reason === "tab-bad-answer" || e.httpStatus >= 500 && e.httpStatus !== 501);
25655
+ if (unknownFate) {
25656
+ throw new CanvasToolError(
25657
+ `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.`
25658
+ );
25659
+ }
25660
+ if (reason === "no-tab") {
25661
+ throw new CanvasToolError(
25662
+ "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."
25663
+ );
25664
+ }
25665
+ if (e.httpStatus === 404 && reason !== "unknown-request") {
25666
+ if (!/board/i.test(e.message)) {
25667
+ throw new CanvasToolError("O servidor do Canvas ainda n\xE3o tem a execu\xE7\xE3o pelo MCP (academy-backend sem a CR1). Nada rodou.");
25668
+ }
25669
+ throw new CanvasToolError("Board n\xE3o encontrado, ou voc\xEA n\xE3o \xE9 membro dele. Confira o id em canvas_list_boards.");
25670
+ }
25671
+ if (e.httpStatus === 403 && (!api.usesPat || /ialab:/.test(e.message))) {
25672
+ throw new CanvasToolError(
25673
+ "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."
25674
+ );
25675
+ }
25676
+ if (e.httpStatus === 403) throw new CanvasToolError(`Sem permiss\xE3o para rodar neste board (s\xF3 dono e editor rodam): ${e.message}.`);
25677
+ if (e.httpStatus === 400) throw new CanvasToolError(`O servidor recusou o pedido: ${e.message}.`);
25678
+ if (e.httpStatus === 429) throw new CanvasToolError("Limite de pedidos de execu\xE7\xE3o (20 por minuto). Espere um minuto.");
25679
+ throw e;
25680
+ }
25681
+ function answerText(a, requestId, max, titleOf) {
25682
+ const est = estimateLines(a.estimate, titleOf);
25683
+ const total = a.estimate ? `${dia(a.estimate.total)} (teto ${fmt2(max)})` : "";
25684
+ const again = a.repeated ? " (a aba j\xE1 tinha recebido este request_id; esta \xE9 a mesma resposta)" : "";
25685
+ if (a.status === "estimate") {
25686
+ return [`Estimativa, nada rodou${again}. Modo ${modeText(a.mode)}. Total agora: ${total}.`, ...est, cabeLine(a, max)].filter(Boolean).join("\n");
25687
+ }
25688
+ if (a.status === "nothing") return `Nada para rodar${again}: ${a.message ?? "o board j\xE1 est\xE1 em dia."}`;
25689
+ const willRun = (a.estimate?.nodes ?? []).filter((n) => n.willRun).map((n) => n.nodeId);
25690
+ return [
25691
+ `Rodando pela sua aba do Canvas${again}. Modo ${modeText(a.mode)}. Estimativa: ${total}.`,
25692
+ ...est,
25693
+ `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.`
25694
+ ].join("\n");
25695
+ }
25696
+ function cabeLine(a, max) {
25697
+ if (!a.estimate) return "";
25698
+ 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.`;
25699
+ }
25700
+ server.registerTool(
25701
+ "canvas_run",
25702
+ {
25703
+ title: "Canvas: rodar n\xF3s",
25704
+ 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.",
25705
+ inputSchema: {
25706
+ board_id: boardId2,
25707
+ 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)."),
25708
+ from_node_id: nodeId.optional().describe("Rodar daqui para frente: este n\xF3 e tudo que depende dele."),
25709
+ all: external_exports.boolean().optional().describe("true: Rodar tudo (s\xF3 o que mudou)."),
25710
+ max_diamonds: external_exports.number().finite().min(0).max(1e6).describe("Teto de diamantes desta execu\xE7\xE3o. Obrigat\xF3rio."),
25711
+ dry_run: external_exports.boolean().optional().describe("true: s\xF3 estima, nada roda."),
25712
+ simulated: external_exports.boolean().optional().describe("true: o pedido \xE9 para o modo Simulado (a aba precisa estar nele)."),
25713
+ label: external_exports.string().trim().min(1).max(80).optional().describe('Quem pede, como a aba mostra ("Rodando a pedido de \u2026").'),
25714
+ request_id: external_exports.string().uuid().optional().describe("S\xF3 para repetir um pedido cujo destino ficou incerto.")
25715
+ },
25716
+ annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }
25717
+ },
25718
+ async (args) => run(async () => {
25719
+ const target = targetOf(args);
25720
+ const requestId = (args.request_id ?? randomUUID3()).toLowerCase();
25721
+ const label = args.label ?? prettyClient(server.server.getClientVersion()?.name);
25722
+ let answer;
25723
+ try {
25724
+ answer = await api.runRequest(args.board_id, {
25725
+ requestId,
25726
+ target,
25727
+ maxDiamonds: args.max_diamonds,
25728
+ dryRun: args.dry_run === true,
25729
+ simulated: args.simulated === true,
25730
+ label
25731
+ });
25732
+ } catch (e) {
25733
+ explain(e, requestId);
25734
+ }
25735
+ const titleOf = await titles(args.board_id);
25736
+ if (answer.ok !== true) {
25737
+ const est = estimateLines(answer.estimate, titleOf);
25738
+ throw new CanvasToolError(
25739
+ [`A aba recusou, nada rodou (${answer.reason ?? "sem motivo"}): ${answer.message ?? ""}`.trim(), ...est].join("\n")
25740
+ );
25741
+ }
25742
+ const nodeIds = (answer.estimate?.nodes ?? []).filter((n) => n.willRun).map((n) => n.nodeId);
25743
+ if (answer.status === "started") {
25744
+ remember(requestId, {
25745
+ boardId: args.board_id,
25746
+ nodeIds: nodeIds.length ? nodeIds : target.kind === "nodes" ? target.nodeIds : target.kind === "from" ? [target.nodeId] : []
25747
+ });
25748
+ }
25749
+ return answerText(answer, requestId, args.max_diamonds, titleOf);
25750
+ })
25751
+ );
25752
+ server.registerTool(
25753
+ "canvas_run_status",
25754
+ {
25755
+ title: "Canvas: andamento da execu\xE7\xE3o",
25756
+ 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.",
25757
+ inputSchema: {
25758
+ board_id: boardId2,
25759
+ request_id: external_exports.string().uuid().describe("O request_id que o canvas_run devolveu."),
25760
+ 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.")
25761
+ },
25762
+ annotations: { readOnlyHint: true, openWorldHint: true }
25763
+ },
25764
+ async ({ board_id, request_id, node_ids }) => run(async () => {
25765
+ const requestId = request_id.toLowerCase();
25766
+ let why = "";
25767
+ try {
25768
+ return statusText(await api.runStatus(board_id, requestId), await titles(board_id));
25769
+ } catch (e) {
25770
+ const reason = errorRaw(e)?.reason;
25771
+ if (!(e instanceof ApiError) || reason !== "unknown-request" && reason !== "no-tab") explain(e);
25772
+ why = reason === "no-tab" ? "Nenhuma aba do Canvas sua est\xE1 aberta neste board." : "Nenhuma aba aberta conhece este pedido.";
25773
+ }
25774
+ const ids = node_ids ?? remembered.get(requestId)?.nodeIds;
25775
+ if (!ids?.length) {
25776
+ 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).`;
25777
+ }
25778
+ return `${why} Pelo estado de execu\xE7\xE3o do board:
25779
+ ${fromRuns(await api.snapshot(board_id), ids)}`;
25780
+ })
25781
+ );
25782
+ }
25783
+ var STATE_TEXT = {
25784
+ preparing: "a aba est\xE1 estimando e conferindo",
25785
+ running: "rodando",
25786
+ done: "terminou",
25787
+ failed: "terminou com erro",
25788
+ cancelled: "cancelado",
25789
+ refused: "recusado",
25790
+ nothing: "nada para rodar",
25791
+ estimate: "s\xF3 estimativa"
25792
+ };
25793
+ var NODE_TEXT = {
25794
+ idle: "parado",
25795
+ waiting: "esperando o montante",
25796
+ queued: "na fila",
25797
+ running: "rodando",
25798
+ done: "pronto",
25799
+ error: "erro",
25800
+ cancelled: "cancelado",
25801
+ skipped: "pulado (o montante falhou)",
25802
+ blocked: "esperando aprova\xE7\xE3o",
25803
+ claimed: "outra pessoa est\xE1 rodando",
25804
+ cached: "em cache"
25805
+ };
25806
+ function statusText(a, titleOf) {
25807
+ const head = [`Pedido ${a.requestId}: ${STATE_TEXT[a.state] ?? a.state}.`];
25808
+ if (a.mode) head.push(`Modo ${modeText(a.mode)}.`);
25809
+ if (typeof a.estimated === "number") head.push(`Estimado ${dia(a.estimated)}.`);
25810
+ if (typeof a.spent === "number") head.push(`Gasto at\xE9 agora ${dia(a.spent)}.`);
25811
+ const lines = [head.join(" ")];
25812
+ if (a.message) lines.push(a.message);
25813
+ for (const n of a.nodes ?? []) {
25814
+ const extra = n.error ? `: ${n.error}` : n.message ? `: ${n.message}` : "";
25815
+ const outs = typeof n.outputs === "number" && n.outputs > 0 ? ` (${n.outputs} sa\xEDda(s))` : "";
25816
+ lines.push(`- ${n.title ?? titleOf(n.nodeId)} (${n.nodeId}): ${NODE_TEXT[n.status] ?? n.status}${outs}${extra}`);
25817
+ }
25818
+ if (a.state === "done" || a.state === "failed") lines.push("Leia o resultado com canvas_read_board (node_ids).");
25819
+ return lines.join("\n");
25820
+ }
25821
+ function fromRuns(snap, ids) {
25822
+ const byNode = new Map(snap.runs.map((r) => [r.nodeId, r]));
25823
+ return ids.map((id) => {
25824
+ const node = snap.doc.nodes[id];
25825
+ const name = node ? nodeName(node) : id;
25826
+ if (!node) return `- ${id}: o n\xF3 n\xE3o existe mais no board`;
25827
+ const r = byNode.get(id);
25828
+ const st = r?.state ?? null;
25829
+ if (r?.claimedBy) return `- ${name} (${id}): rodando agora`;
25830
+ 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)`;
25831
+ const outs = Object.values(st.outputs ?? {}).reduce((sum, list) => sum + (Array.isArray(list) ? list.length : 0), 0);
25832
+ const err = typeof st.error === "string" ? st.error : st.error?.message;
25833
+ return `- ${name} (${id}): ${NODE_TEXT[st.status] ?? st.status}${outs ? ` (${outs} sa\xEDda(s))` : ""}${err ? `: ${err}` : ""}`;
25834
+ }).join("\n");
25835
+ }
25836
+
25583
25837
  // src/canvas/mentions.ts
25584
25838
  var VIA_MENTION = "viaMention";
25585
25839
  var PROMPT_REFS_INPUT = "refs";
@@ -25737,7 +25991,7 @@ function legacySequenceGuard(node, key) {
25737
25991
  }
25738
25992
  var SECTION_Z = -1e3;
25739
25993
  var GAP = 80;
25740
- var boardId2 = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
25994
+ var boardId3 = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
25741
25995
  var nodeIdArg2 = external_exports.string().min(1).max(128);
25742
25996
  var point = external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() });
25743
25997
  var LABEL_TEXT_SIZES = ["titulo", "subtitulo", "corpo"];
@@ -25870,9 +26124,9 @@ ${lines.join("\n")}
25870
26124
  return `\u2705 ${done} em ${envs.length} envelope(s) (seq at\xE9 ${last}).
25871
26125
  ${lines.join("\n")}${note}`;
25872
26126
  }
25873
- function needNode(doc, nodeId) {
25874
- const node = doc.nodes[nodeId];
25875
- if (!node) throw new CanvasToolError(`N\xF3 ${nodeId} n\xE3o existe no board. Releia com canvas_read_board.`);
26127
+ function needNode(doc, nodeId2) {
26128
+ const node = doc.nodes[nodeId2];
26129
+ if (!node) throw new CanvasToolError(`N\xF3 ${nodeId2} n\xE3o existe no board. Releia com canvas_read_board.`);
25876
26130
  return node;
25877
26131
  }
25878
26132
  function portWarning(doc, source, sourcePort, target, targetPort) {
@@ -25987,7 +26241,7 @@ ${lines.join("\n")}${note}`;
25987
26241
  title: "Canvas: ler board",
25988
26242
  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
26243
  inputSchema: {
25990
- board_id: boardId2,
26244
+ board_id: boardId3,
25991
26245
  node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados).")
25992
26246
  },
25993
26247
  annotations: { readOnlyHint: true, openWorldHint: false }
@@ -26009,7 +26263,7 @@ ${lines.join("\n")}${note}`;
26009
26263
  title: "Canvas: ler n\xF3 inteiro",
26010
26264
  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
26265
  inputSchema: {
26012
- board_id: boardId2,
26266
+ board_id: boardId3,
26013
26267
  node_id: nodeIdArg2,
26014
26268
  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
26269
  offset: external_exports.number().int().min(0).optional().describe("In\xEDcio da parte, em caracteres (padr\xE3o 0)."),
@@ -26118,7 +26372,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26118
26372
  title: "Canvas: adicionar n\xF3",
26119
26373
  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
26374
  inputSchema: {
26121
- board_id: boardId2,
26375
+ board_id: boardId3,
26122
26376
  kind: external_exports.string().min(1).max(40),
26123
26377
  label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
26124
26378
  data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
@@ -26175,7 +26429,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26175
26429
  title: "Canvas: editar n\xF3",
26176
26430
  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
26431
  inputSchema: {
26178
- board_id: boardId2,
26432
+ board_id: boardId3,
26179
26433
  node_id: nodeIdArg2,
26180
26434
  data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave."),
26181
26435
  label: external_exports.string().trim().min(1).max(120).optional(),
@@ -26203,7 +26457,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26203
26457
  title: "Canvas: editar v\xE1rios n\xF3s",
26204
26458
  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
26459
  inputSchema: {
26206
- board_id: boardId2,
26460
+ board_id: boardId3,
26207
26461
  nodes: external_exports.array(
26208
26462
  external_exports.object({
26209
26463
  node_id: nodeIdArg2,
@@ -26255,7 +26509,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26255
26509
  title: "Canvas: gravar por caminho",
26256
26510
  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
26511
  inputSchema: {
26258
- board_id: boardId2,
26512
+ board_id: boardId3,
26259
26513
  node_id: nodeIdArg2,
26260
26514
  sets: external_exports.array(
26261
26515
  external_exports.object({
@@ -26354,7 +26608,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26354
26608
  title: "Canvas: ligar portas",
26355
26609
  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
26610
  inputSchema: {
26357
- board_id: boardId2,
26611
+ board_id: boardId3,
26358
26612
  source: nodeIdArg2,
26359
26613
  source_port: external_exports.string().min(1).max(80),
26360
26614
  target: nodeIdArg2,
@@ -26381,7 +26635,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26381
26635
  title: "Canvas: apagar n\xF3s/liga\xE7\xF5es",
26382
26636
  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
26637
  inputSchema: {
26384
- board_id: boardId2,
26638
+ board_id: boardId3,
26385
26639
  node_ids: external_exports.array(nodeIdArg2).max(200).optional(),
26386
26640
  edge_ids: external_exports.array(nodeIdArg2).max(200).optional()
26387
26641
  },
@@ -26425,7 +26679,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26425
26679
  {
26426
26680
  title: "Canvas: renomear board",
26427
26681
  description: `Troca o t\xEDtulo do board (board.set). ${READ}Precisa de papel de editor ou dono.`,
26428
- inputSchema: { board_id: boardId2, title: external_exports.string().trim().min(1).max(200) },
26682
+ inputSchema: { board_id: boardId3, title: external_exports.string().trim().min(1).max(200) },
26429
26683
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
26430
26684
  },
26431
26685
  async ({ board_id, title }) => run(
@@ -26441,7 +26695,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26441
26695
  {
26442
26696
  title: "Canvas: membros do board",
26443
26697
  description: "Lista quem tem acesso ao board (userId, nome, papel). S\xF3 leitura.",
26444
- inputSchema: { board_id: boardId2 },
26698
+ inputSchema: { board_id: boardId3 },
26445
26699
  annotations: { readOnlyHint: true, openWorldHint: false }
26446
26700
  },
26447
26701
  async ({ board_id }) => run(async () => {
@@ -26458,7 +26712,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26458
26712
  title: "Canvas: adicionar membro",
26459
26713
  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
26714
  inputSchema: {
26461
- board_id: boardId2,
26715
+ board_id: boardId3,
26462
26716
  user_id: external_exports.string().uuid(),
26463
26717
  role: external_exports.enum(["editor", "viewer"]).describe("editor edita; viewer s\xF3 v\xEA.")
26464
26718
  },
@@ -26474,7 +26728,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26474
26728
  {
26475
26729
  title: "Canvas: remover membro",
26476
26730
  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: boardId2, user_id: external_exports.string().uuid() },
26731
+ inputSchema: { board_id: boardId3, user_id: external_exports.string().uuid() },
26478
26732
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
26479
26733
  },
26480
26734
  async ({ board_id, user_id }) => run(async () => {
@@ -26485,13 +26739,14 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26485
26739
  registerCanvasCommentTools(server, api, run);
26486
26740
  registerCanvasMediaTools(server, { api, cfg, run, edit, seen, placeFor });
26487
26741
  registerCanvasLayoutTools(server, { api, run, edit });
26742
+ registerCanvasRunTools(server, api, run);
26488
26743
  }
26489
26744
 
26490
26745
  // src/version.ts
26491
26746
  import { createRequire } from "module";
26492
26747
  var require2 = createRequire(import.meta.url);
26493
26748
  function readVersion() {
26494
- if ("0.30.0") return "0.30.0";
26749
+ if ("0.31.0") return "0.31.0";
26495
26750
  try {
26496
26751
  const pkg = require2("../package.json");
26497
26752
  return pkg.version ?? "0.0.0-unknown";
@@ -26506,7 +26761,7 @@ var SERVER_INSTRUCTIONS = [
26506
26761
  "BePlus: tudo roda pela conta do usu\xE1rio (diamantes, limites e hist\xF3rico na plataforma).",
26507
26762
  '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
26763
  "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 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.",
26764
+ "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.",
26510
26765
  '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
26766
  '"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
26767
  ].join("\n");