beplus-mcp 0.29.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"]
@@ -22141,7 +22141,9 @@ var IMAGE_MODELS = [
22141
22141
  "midjourney"
22142
22142
  ];
22143
22143
  var SEEDREAM_LITE_SIZES = ["2K", "3K", "4K"];
22144
- var SEEDREAM_PRO_SIZES = ["1K", "2K"];
22144
+ var SEEDREAM_PRO_SIZES = ["1K", "1.5K", "2K"];
22145
+ var SEEDREAM_PRO_PIXELS = { min: 921600, max: 4624220 };
22146
+ var SEEDREAM_PRO_MAX_IMAGES = 4;
22145
22147
  var VIDEO_MODELS = [
22146
22148
  "bytedance/seedance-2.5",
22147
22149
  "bytedance/seedance-2.0",
@@ -22263,17 +22265,25 @@ function registerGenerationTools(server, client, cfg) {
22263
22265
  // que o modelo aceita (Seedream/nano-banana não têm limite publicado).
22264
22266
  prompt: external_exports.string().min(1).describe("Descri\xE7\xE3o da imagem desejada."),
22265
22267
  model: external_exports.enum(IMAGE_MODELS).default(DEFAULTS.imageModel).describe(
22266
- "nano-banana-2 = barato/r\xE1pido; nano-banana-pro = melhor qualidade; gpt-image-2 = OpenAI; gpt-image-1 = OpenAI, \xDANICO que faz fundo transparente de verdade (PNG/WebP com alpha) \u2014 o gpt-image-2 N\xC3O faz, use este pra recorte/sticker; seedream-5.0-lite = mais barato do cat\xE1logo, \xFAnico que chega a 4K, gera s\xE9ries e mant\xE9m o ESTILO da imagem de refer\xEAncia; seedream-5.0-pro = acabamento fotorrealista, s\xF3 at\xE9 2K \u2014 preserva o personagem da refer\xEAncia mas costuma reinterpretar o estilo (um vetor flat volta em 3D)."
22268
+ "nano-banana-2 = barato/r\xE1pido; nano-banana-pro = melhor qualidade; gpt-image-2 = OpenAI; gpt-image-1 = OpenAI, \xDANICO que faz fundo transparente de verdade (PNG/WebP com alpha) \u2014 o gpt-image-2 N\xC3O faz, use este pra recorte/sticker; seedream-5.0-lite = mais barato do cat\xE1logo, \xFAnico que chega a 4K, gera s\xE9ries e mant\xE9m o ESTILO da imagem de refer\xEAncia; seedream-5.0-pro = acabamento fotorrealista, s\xF3 at\xE9 2K (1K/1.5K/2K, propor\xE7\xE3o, tamanho livre e at\xE9 4 imagens por pedido) \u2014 preserva o personagem da refer\xEAncia mas costuma reinterpretar o estilo (um vetor flat volta em 3D)."
22269
+ ),
22270
+ aspect_ratio: external_exports.enum(ASPECT_RATIOS).optional().describe(
22271
+ "Propor\xE7\xE3o. nano-banana: padr\xE3o 1:1. Seedream Pro: combina com size (1K/1.5K/2K) e vira LARGURAxALTURA no backend; sem ela o modelo escolhe o formato pelo prompt."
22267
22272
  ),
22268
- aspect_ratio: external_exports.enum(ASPECT_RATIOS).optional().describe("Propor\xE7\xE3o (nano-banana). Padr\xE3o 1:1."),
22269
22273
  resolution: external_exports.enum(IMAGE_RESOLUTIONS).optional().describe("Resolu\xE7\xE3o (nano-banana): 1K/2K/4K. Padr\xE3o 1K."),
22270
22274
  reference_images: external_exports.array(external_exports.string().min(1)).max(16).optional().describe(
22271
22275
  "Imagens de refer\xEAncia (image-to-image): URL p\xFAblica OU caminho de arquivo local (o MCP faz upload pro R2 automaticamente). At\xE9 14 (nano-banana) / 16 (gpt-image-1/2)."
22272
22276
  ),
22273
22277
  google_search: external_exports.boolean().optional().describe("nano-banana-2: aterra a imagem em busca web do Google (fatos atuais). Padr\xE3o false."),
22274
22278
  image_search: external_exports.boolean().optional().describe("nano-banana-2: usa busca de imagens do Google como refer\xEAncia visual. Padr\xE3o false."),
22275
- size: external_exports.enum(["1024x1024", "1536x1024", "1024x1536", "auto", "1K", "2K", "3K", "4K"]).optional().describe(
22276
- "Tamanho. gpt-image-1/2: 1024x1024/1536x1024/1024x1536/auto. Seedream Lite: 2K/3K/4K (padr\xE3o 2K). Seedream Pro: 1K/2K apenas \u2014 o Pro N\xC3O faz 3K/4K."
22279
+ size: external_exports.union([
22280
+ external_exports.enum(["1024x1024", "1536x1024", "1024x1536", "auto", "1K", "1.5K", "2K", "3K", "4K"]),
22281
+ external_exports.string().regex(/^\d{3,5}x\d{3,5}$/, "use LARGURAxALTURA, ex.: 2048x1152")
22282
+ ]).optional().describe(
22283
+ "Tamanho. gpt-image-1/2: 1024x1024/1536x1024/1024x1536/auto. Seedream Lite: 2K/3K/4K (padr\xE3o 2K). Seedream Pro: 1K/1.5K/2K ou LARGURAxALTURA livre (\xE1rea entre 921.600 e 4.624.220 pixels, propor\xE7\xE3o de 1:16 a 16:1). O Pro N\xC3O faz 3K/4K."
22284
+ ),
22285
+ num_images: external_exports.number().int().min(1).max(SEEDREAM_PRO_MAX_IMAGES).optional().describe(
22286
+ "Seedream 5.0 PRO apenas: quantas imagens gerar no mesmo pedido (1-4), em chamadas paralelas. Cada imagem \xE9 cobrada; as que falharem s\xE3o estornadas."
22277
22287
  ),
22278
22288
  batch_images: external_exports.number().int().min(2).max(15).optional().describe(
22279
22289
  "Seedream 5.0 LITE apenas: gera uma s\xE9rie de imagens relacionadas numa chamada (2-15). Cada imagem \xE9 cobrada. O Seedream Pro N\xC3O suporta \u2014 passar aqui devolve erro."
@@ -22314,7 +22324,19 @@ function registerGenerationTools(server, client, cfg) {
22314
22324
  const isSeedream = isSeedreamLite || isSeedreamPro;
22315
22325
  if (isSeedream) {
22316
22326
  const allowed = isSeedreamPro ? SEEDREAM_PRO_SIZES : SEEDREAM_LITE_SIZES;
22317
- if (args.size && !allowed.includes(args.size)) {
22327
+ const free = args.size?.match(/^(\d+)x(\d+)$/);
22328
+ if (isSeedreamPro && free) {
22329
+ const w = Number(free[1]);
22330
+ const h = Number(free[2]);
22331
+ if (w * h < SEEDREAM_PRO_PIXELS.min || w * h > SEEDREAM_PRO_PIXELS.max) {
22332
+ return errorResult(
22333
+ `size ${args.size} d\xE1 ${w * h} pixels; o Pro aceita entre ${SEEDREAM_PRO_PIXELS.min} e ${SEEDREAM_PRO_PIXELS.max} (ex.: 1280x720 a 2048x2048).`
22334
+ );
22335
+ }
22336
+ if (w / h > 16 || h / w > 16) {
22337
+ return errorResult(`size ${args.size}: a propor\xE7\xE3o precisa ficar entre 1:16 e 16:1.`);
22338
+ }
22339
+ } else if (args.size && !allowed.includes(args.size)) {
22318
22340
  return errorResult(
22319
22341
  `${args.model} aceita size ${allowed.join("/")} (voc\xEA pediu ${args.size}). ` + (isSeedreamPro ? "O Pro n\xE3o faz 3K/4K \u2014 use o seedream-5.0-lite para resolu\xE7\xF5es maiores." : "O Lite n\xE3o faz 1K \u2014 use o seedream-5.0-pro se quiser a resolu\xE7\xE3o menor.")
22320
22342
  );
@@ -22336,6 +22358,11 @@ function registerGenerationTools(server, client, cfg) {
22336
22358
  } else if (args.batch_images) {
22337
22359
  return errorResult("batch_images s\xF3 existe no bytedance/seedream-5.0-lite.");
22338
22360
  }
22361
+ if (args.num_images && args.num_images > 1 && !isSeedreamPro) {
22362
+ return errorResult(
22363
+ "num_images s\xF3 existe no bytedance/seedream-5.0-pro. No seedream-5.0-lite use batch_images; nos outros modelos, chame generate_image mais vezes."
22364
+ );
22365
+ }
22339
22366
  if (args.background === "transparent") {
22340
22367
  if (args.model !== "openai/gpt-image-1") {
22341
22368
  return errorResult(
@@ -22365,6 +22392,7 @@ function registerGenerationTools(server, client, cfg) {
22365
22392
  // amigável exposto aqui.
22366
22393
  sequential_image_generation: isSeedreamLite && args.batch_images ? "auto" : void 0,
22367
22394
  max_images: isSeedreamLite && args.batch_images ? args.batch_images : void 0,
22395
+ num_images: isSeedreamPro && args.num_images && args.num_images > 1 ? args.num_images : void 0,
22368
22396
  quality: isMidjourney ? args.mj_quality : args.quality,
22369
22397
  background: args.background,
22370
22398
  moderation: args.moderation,
@@ -22987,15 +23015,15 @@ function probeJpeg(b) {
22987
23015
  function probeWebp(b) {
22988
23016
  if (b.length < 30) return null;
22989
23017
  if (b.toString("ascii", 0, 4) !== "RIFF" || b.toString("ascii", 8, 12) !== "WEBP") return null;
22990
- const fmt2 = b.toString("ascii", 12, 16);
22991
- if (fmt2 === "VP8 ") {
23018
+ const fmt3 = b.toString("ascii", 12, 16);
23019
+ if (fmt3 === "VP8 ") {
22992
23020
  return { width: b.readUInt16LE(26) & 16383, height: b.readUInt16LE(28) & 16383 };
22993
23021
  }
22994
- if (fmt2 === "VP8L") {
23022
+ if (fmt3 === "VP8L") {
22995
23023
  const bits = b.readUInt32LE(21);
22996
23024
  return { width: (bits & 16383) + 1, height: (bits >> 14 & 16383) + 1 };
22997
23025
  }
22998
- if (fmt2 === "VP8X") {
23026
+ if (fmt3 === "VP8X") {
22999
23027
  const w = b[24] | b[25] << 8 | b[26] << 16;
23000
23028
  const h = b[27] | b[28] << 8 | b[29] << 16;
23001
23029
  return { width: w + 1, height: h + 1 };
@@ -23671,6 +23699,8 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
23671
23699
  mode: external_exports.string().optional(),
23672
23700
  size: external_exports.string().optional(),
23673
23701
  quality: external_exports.string().optional(),
23702
+ aspect_ratio: external_exports.string().optional().describe("Seedream Pro: a propor\xE7\xE3o muda os pixels e a faixa de pre\xE7o."),
23703
+ num_images: external_exports.number().int().min(1).optional().describe("Seedream Pro: imagens por pedido; cada uma \xE9 cobrada."),
23674
23704
  // Upscale: o preço não sai de uma chave fixa — vem das medidas do
23675
23705
  // arquivo (megapixel de saída na imagem, bloco de 10s no vídeo). Sem
23676
23706
  // elas o backend responde pelo padrão de 1024²/10s, que não é o preço
@@ -23712,6 +23742,8 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
23712
23742
  mode: args.mode,
23713
23743
  size: args.size,
23714
23744
  quality: args.quality,
23745
+ aspect_ratio: args.aspect_ratio,
23746
+ num_images: args.num_images,
23715
23747
  upscale_factor: args.upscale_factor,
23716
23748
  model_variant: args.model_variant,
23717
23749
  source_width: width2,
@@ -24054,6 +24086,7 @@ import { createHash } from "crypto";
24054
24086
 
24055
24087
  // src/canvas/api.ts
24056
24088
  var PINNED_CORE_VERSION = "ca7b3b406184c7f679f555c3a008013197182b23";
24089
+ var RUN_REQUEST_TIMEOUT_MS = 4e4;
24057
24090
  function errorRaw(err) {
24058
24091
  if (!(err instanceof ApiError)) return void 0;
24059
24092
  const raw = err.body?.error?.raw;
@@ -24094,13 +24127,13 @@ var CanvasApi = class {
24094
24127
  createBoard(body2) {
24095
24128
  return this.req("POST", "/boards", { body: body2 });
24096
24129
  }
24097
- snapshot(boardId3) {
24098
- return this.req("GET", `/boards/${boardId3}`);
24130
+ snapshot(boardId4) {
24131
+ return this.req("GET", `/boards/${boardId4}`);
24099
24132
  }
24100
24133
  /** Replay depois de `after`. `null` quando o servidor manda reler a foto (410). */
24101
- async opsAfter(boardId3, after) {
24134
+ async opsAfter(boardId4, after) {
24102
24135
  try {
24103
- const { envelopes } = await this.req("GET", `/boards/${boardId3}/ops`, {
24136
+ const { envelopes } = await this.req("GET", `/boards/${boardId4}/ops`, {
24104
24137
  query: { after }
24105
24138
  });
24106
24139
  return envelopes;
@@ -24109,8 +24142,8 @@ var CanvasApi = class {
24109
24142
  throw e;
24110
24143
  }
24111
24144
  }
24112
- async commit(boardId3, ops) {
24113
- const send = () => this.req("POST", `/boards/${boardId3}/ops`, {
24145
+ async commit(boardId4, ops) {
24146
+ const send = () => this.req("POST", `/boards/${boardId4}/ops`, {
24114
24147
  body: { coreVersion: this.coreVersion, ops }
24115
24148
  });
24116
24149
  try {
@@ -24131,18 +24164,18 @@ var CanvasApi = class {
24131
24164
  }
24132
24165
  }
24133
24166
  }
24134
- members(boardId3) {
24135
- return this.req("GET", `/boards/${boardId3}/members`);
24167
+ members(boardId4) {
24168
+ return this.req("GET", `/boards/${boardId4}/members`);
24136
24169
  }
24137
- putMember(boardId3, userId, role) {
24138
- 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 } });
24139
24172
  }
24140
- removeMember(boardId3, userId) {
24141
- return this.req("DELETE", `/boards/${boardId3}/members/${userId}`);
24173
+ removeMember(boardId4, userId) {
24174
+ return this.req("DELETE", `/boards/${boardId4}/members/${userId}`);
24142
24175
  }
24143
24176
  // ── comentários (academy-backend #313) ────────────────────────────────────
24144
- listComments(boardId3, status = "open") {
24145
- return this.req("GET", `/boards/${boardId3}/comments`, { query: { status } });
24177
+ listComments(boardId4, status = "open") {
24178
+ return this.req("GET", `/boards/${boardId4}/comments`, { query: { status } });
24146
24179
  }
24147
24180
  /**
24148
24181
  * Escrita de comentário passa por aqui: o back limita a 60 por minuto por pessoa
@@ -24168,14 +24201,25 @@ var CanvasApi = class {
24168
24201
  }
24169
24202
  }
24170
24203
  /** Raiz ou resposta. O back devolve sempre a THREAD DA RAIZ (resposta nova = última de `replies`). */
24171
- createComment(boardId3, body2) {
24172
- return this.write("POST", `/boards/${boardId3}/comments`, body2);
24204
+ createComment(boardId4, body2) {
24205
+ return this.write("POST", `/boards/${boardId4}/comments`, body2);
24173
24206
  }
24174
- resolveComment(boardId3, commentId2) {
24175
- return this.write("POST", `/boards/${boardId3}/comments/${commentId2}/resolve`);
24207
+ resolveComment(boardId4, commentId2) {
24208
+ return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/resolve`);
24176
24209
  }
24177
- reopenComment(boardId3, commentId2) {
24178
- 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}`);
24179
24223
  }
24180
24224
  /** Só para os testes conferirem o aprendizado. */
24181
24225
  get currentCoreVersion() {
@@ -24451,8 +24495,8 @@ function checkOp(op) {
24451
24495
  return null;
24452
24496
  }
24453
24497
  }
24454
- function envelope(boardId3, op, actorId) {
24455
- 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 };
24456
24500
  }
24457
24501
  function checkEnvelope(env) {
24458
24502
  const bad = checkOp(env.op);
@@ -24507,13 +24551,13 @@ function makeEdge(init) {
24507
24551
  }
24508
24552
  var setValue = (value) => ({ kind: "value", value });
24509
24553
  var unsetValue = () => ({ kind: "unset" });
24510
- function removeNodeOps(doc, nodeId) {
24554
+ function removeNodeOps(doc, nodeId2) {
24511
24555
  const ops = [];
24512
- 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();
24513
24557
  for (const edgeId of edgeIds) ops.push({ type: "edge.remove", edgeId });
24514
- const history = Object.hasOwn(doc.nodeHistory, nodeId) ? doc.nodeHistory[nodeId] : void 0;
24515
- if (history) for (const resultId of [...history].reverse()) ops.push({ type: "history.remove", nodeId, resultId });
24516
- 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 });
24517
24561
  return ops;
24518
24562
  }
24519
24563
  function batchOf(ops) {
@@ -24529,14 +24573,14 @@ var OpTooLargeError = class extends Error {
24529
24573
  op;
24530
24574
  bytes;
24531
24575
  };
24532
- function packEnvelopes(boardId3, ops, actorId, limit = MAX_OP_BYTES - 1024) {
24576
+ function packEnvelopes(boardId4, ops, actorId, limit = MAX_OP_BYTES - 1024) {
24533
24577
  const bytes = (v) => Buffer.byteLength(JSON.stringify(v));
24534
- const overhead = bytes(envelope(boardId3, { type: "batch", ops: [] }, actorId));
24578
+ const overhead = bytes(envelope(boardId4, { type: "batch", ops: [] }, actorId));
24535
24579
  const out = [];
24536
24580
  let cur = [];
24537
24581
  let size = overhead;
24538
24582
  const flush = () => {
24539
- if (cur.length) out.push(envelope(boardId3, batchOf(cur), actorId));
24583
+ if (cur.length) out.push(envelope(boardId4, batchOf(cur), actorId));
24540
24584
  cur = [];
24541
24585
  size = overhead;
24542
24586
  };
@@ -24914,8 +24958,8 @@ function mmss(t) {
24914
24958
  const pad = (n) => String(n).padStart(2, "0");
24915
24959
  return h ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
24916
24960
  }
24917
- function nodeRef(doc, nodeId) {
24918
- const node = doc.nodes[nodeId];
24961
+ function nodeRef(doc, nodeId2) {
24962
+ const node = doc.nodes[nodeId2];
24919
24963
  const kind = nodeKind(node) ?? node.type;
24920
24964
  return `"${oneLine(nodeName(node), 60)}" (${kind}, ${node.id})`;
24921
24965
  }
@@ -24939,13 +24983,13 @@ function outputKeyOf(entry) {
24939
24983
  if (typeof entry.url === "string" && entry.url !== "") return `url-${hashString(entry.url)}`;
24940
24984
  return null;
24941
24985
  }
24942
- function versionOf(doc, nodeId, outputKey) {
24943
- const ids = doc.nodeHistory?.[nodeId];
24986
+ function versionOf(doc, nodeId2, outputKey) {
24987
+ const ids = doc.nodeHistory?.[nodeId2];
24944
24988
  if (Array.isArray(ids)) {
24945
24989
  const i = ids.indexOf(outputKey);
24946
24990
  if (i >= 0) return i + 1;
24947
24991
  }
24948
- const history = doc.nodes[nodeId]?.data.history;
24992
+ const history = doc.nodes[nodeId2]?.data.history;
24949
24993
  if (Array.isArray(history)) {
24950
24994
  const i = history.findIndex(
24951
24995
  (h) => typeof h === "object" && h !== null && !Array.isArray(h) && outputKeyOf(h) === outputKey
@@ -25415,17 +25459,17 @@ function holdsOne(doc, node) {
25415
25459
  if (node.data.holds === "many") return false;
25416
25460
  return itemsOf(node).length === 1 && Object.values(doc.edges).some((e) => e.source === node.id);
25417
25461
  }
25418
- function needMediaNode(doc, nodeId) {
25419
- const node = doc.nodes[nodeId];
25420
- 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.`);
25421
25465
  if (nodeKind(node) !== "media") {
25422
25466
  throw new CanvasToolError(
25423
- `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.`
25424
25468
  );
25425
25469
  }
25426
25470
  return node;
25427
25471
  }
25428
- function payload(u, nodeId) {
25472
+ function payload(u, nodeId2) {
25429
25473
  const out = { url: u.url, mime: u.mime, bytes: u.bytes };
25430
25474
  if (u.meta.duration !== void 0) out.duration = u.meta.duration;
25431
25475
  if (u.meta.width !== void 0) out.width = u.meta.width;
@@ -25434,14 +25478,14 @@ function payload(u, nodeId) {
25434
25478
  out.media_item = mediaItem(u);
25435
25479
  if (u.kind !== "file") {
25436
25480
  out.sequence_asset = sequenceAsset(u);
25437
- out.libraryItemId = libraryItemId(u, nodeId);
25481
+ out.libraryItemId = libraryItemId(u, nodeId2);
25438
25482
  }
25439
25483
  return JSON.stringify(out);
25440
25484
  }
25441
- function sequenceHint(u, nodeId) {
25485
+ function sequenceHint(u, nodeId2) {
25442
25486
  if (u.kind === "file") return "Documento: entra no n\xF3 M\xEDdia, n\xE3o na timeline da Montagem.";
25443
25487
  const lines = [
25444
- "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.")
25445
25489
  ];
25446
25490
  if (u.meta.duration !== void 0) {
25447
25491
  const at30 = Math.floor(u.meta.duration * 30 + 1e-9);
@@ -25498,14 +25542,14 @@ URL: ${u.url}`;
25498
25542
  if (!args.board_id) {
25499
25543
  return [head, "", payload(u), "", sequenceHint(u)].join("\n");
25500
25544
  }
25501
- const boardId3 = args.board_id;
25502
- let nodeId = args.node_id;
25545
+ const boardId4 = args.board_id;
25546
+ let nodeId2 = args.node_id;
25503
25547
  let done;
25504
25548
  try {
25505
- done = await edit(boardId3, async (doc) => {
25549
+ done = await edit(boardId4, async (doc) => {
25506
25550
  const item = mediaItem(u);
25507
- if (nodeId) {
25508
- const node = needMediaNode(doc, nodeId);
25551
+ if (nodeId2) {
25552
+ const node = needMediaNode(doc, nodeId2);
25509
25553
  const replacing = args.replace ?? holdsOne(doc, node);
25510
25554
  const items = replacing ? [item] : [...itemsOf(node), item];
25511
25555
  if (items.length > MAX_MEDIA_ITEMS) {
@@ -25527,7 +25571,7 @@ URL: ${u.url}`;
25527
25571
  zIndex: Object.values(doc.nodes).reduce((t, n) => Math.max(t, n.zIndex), 0) + 1,
25528
25572
  createdBy: await api.me()
25529
25573
  });
25530
- nodeId = created.id;
25574
+ nodeId2 = created.id;
25531
25575
  return {
25532
25576
  ops: [{ type: "node.add", nodeId: created.id, node: created }],
25533
25577
  targets: [],
@@ -25543,9 +25587,251 @@ N\xE3o suba de novo: releia com canvas_read_board e grave media_item em data.ite
25543
25587
  ${payload(u)}`
25544
25588
  );
25545
25589
  }
25546
- return [head, done, "", payload(u, nodeId), "", sequenceHint(u, nodeId)].join("\n");
25590
+ return [head, done, "", payload(u, nodeId2), "", sequenceHint(u, nodeId2)].join("\n");
25591
+ })
25592
+ );
25593
+ }
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);
25547
25750
  })
25548
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");
25549
25835
  }
25550
25836
 
25551
25837
  // src/canvas/mentions.ts
@@ -25705,7 +25991,7 @@ function legacySequenceGuard(node, key) {
25705
25991
  }
25706
25992
  var SECTION_Z = -1e3;
25707
25993
  var GAP = 80;
25708
- 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.");
25709
25995
  var nodeIdArg2 = external_exports.string().min(1).max(128);
25710
25996
  var point = external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() });
25711
25997
  var LABEL_TEXT_SIZES = ["titulo", "subtitulo", "corpo"];
@@ -25838,9 +26124,9 @@ ${lines.join("\n")}
25838
26124
  return `\u2705 ${done} em ${envs.length} envelope(s) (seq at\xE9 ${last}).
25839
26125
  ${lines.join("\n")}${note}`;
25840
26126
  }
25841
- function needNode(doc, nodeId) {
25842
- const node = doc.nodes[nodeId];
25843
- 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.`);
25844
26130
  return node;
25845
26131
  }
25846
26132
  function portWarning(doc, source, sourcePort, target, targetPort) {
@@ -25955,7 +26241,7 @@ ${lines.join("\n")}${note}`;
25955
26241
  title: "Canvas: ler board",
25956
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.',
25957
26243
  inputSchema: {
25958
- board_id: boardId2,
26244
+ board_id: boardId3,
25959
26245
  node_ids: external_exports.array(nodeIdArg2).max(20).optional().describe("N\xF3s para ver o data (sem blobs, cortes marcados).")
25960
26246
  },
25961
26247
  annotations: { readOnlyHint: true, openWorldHint: false }
@@ -25977,7 +26263,7 @@ ${lines.join("\n")}${note}`;
25977
26263
  title: "Canvas: ler n\xF3 inteiro",
25978
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,
25979
26265
  inputSchema: {
25980
- board_id: boardId2,
26266
+ board_id: boardId3,
25981
26267
  node_id: nodeIdArg2,
25982
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.'),
25983
26269
  offset: external_exports.number().int().min(0).optional().describe("In\xEDcio da parte, em caracteres (padr\xE3o 0)."),
@@ -26086,7 +26372,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26086
26372
  title: "Canvas: adicionar n\xF3",
26087
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,
26088
26374
  inputSchema: {
26089
- board_id: boardId2,
26375
+ board_id: boardId3,
26090
26376
  kind: external_exports.string().min(1).max(40),
26091
26377
  label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
26092
26378
  data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
@@ -26143,7 +26429,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26143
26429
  title: "Canvas: editar n\xF3",
26144
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,
26145
26431
  inputSchema: {
26146
- board_id: boardId2,
26432
+ board_id: boardId3,
26147
26433
  node_id: nodeIdArg2,
26148
26434
  data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave."),
26149
26435
  label: external_exports.string().trim().min(1).max(120).optional(),
@@ -26171,7 +26457,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26171
26457
  title: "Canvas: editar v\xE1rios n\xF3s",
26172
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.',
26173
26459
  inputSchema: {
26174
- board_id: boardId2,
26460
+ board_id: boardId3,
26175
26461
  nodes: external_exports.array(
26176
26462
  external_exports.object({
26177
26463
  node_id: nodeIdArg2,
@@ -26223,7 +26509,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26223
26509
  title: "Canvas: gravar por caminho",
26224
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,
26225
26511
  inputSchema: {
26226
- board_id: boardId2,
26512
+ board_id: boardId3,
26227
26513
  node_id: nodeIdArg2,
26228
26514
  sets: external_exports.array(
26229
26515
  external_exports.object({
@@ -26322,7 +26608,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26322
26608
  title: "Canvas: ligar portas",
26323
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.`,
26324
26610
  inputSchema: {
26325
- board_id: boardId2,
26611
+ board_id: boardId3,
26326
26612
  source: nodeIdArg2,
26327
26613
  source_port: external_exports.string().min(1).max(80),
26328
26614
  target: nodeIdArg2,
@@ -26349,7 +26635,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26349
26635
  title: "Canvas: apagar n\xF3s/liga\xE7\xF5es",
26350
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.`,
26351
26637
  inputSchema: {
26352
- board_id: boardId2,
26638
+ board_id: boardId3,
26353
26639
  node_ids: external_exports.array(nodeIdArg2).max(200).optional(),
26354
26640
  edge_ids: external_exports.array(nodeIdArg2).max(200).optional()
26355
26641
  },
@@ -26393,7 +26679,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26393
26679
  {
26394
26680
  title: "Canvas: renomear board",
26395
26681
  description: `Troca o t\xEDtulo do board (board.set). ${READ}Precisa de papel de editor ou dono.`,
26396
- 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) },
26397
26683
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
26398
26684
  },
26399
26685
  async ({ board_id, title }) => run(
@@ -26409,7 +26695,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26409
26695
  {
26410
26696
  title: "Canvas: membros do board",
26411
26697
  description: "Lista quem tem acesso ao board (userId, nome, papel). S\xF3 leitura.",
26412
- inputSchema: { board_id: boardId2 },
26698
+ inputSchema: { board_id: boardId3 },
26413
26699
  annotations: { readOnlyHint: true, openWorldHint: false }
26414
26700
  },
26415
26701
  async ({ board_id }) => run(async () => {
@@ -26426,7 +26712,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26426
26712
  title: "Canvas: adicionar membro",
26427
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.",
26428
26714
  inputSchema: {
26429
- board_id: boardId2,
26715
+ board_id: boardId3,
26430
26716
  user_id: external_exports.string().uuid(),
26431
26717
  role: external_exports.enum(["editor", "viewer"]).describe("editor edita; viewer s\xF3 v\xEA.")
26432
26718
  },
@@ -26442,7 +26728,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26442
26728
  {
26443
26729
  title: "Canvas: remover membro",
26444
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.",
26445
- inputSchema: { board_id: boardId2, user_id: external_exports.string().uuid() },
26731
+ inputSchema: { board_id: boardId3, user_id: external_exports.string().uuid() },
26446
26732
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
26447
26733
  },
26448
26734
  async ({ board_id, user_id }) => run(async () => {
@@ -26453,13 +26739,14 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
26453
26739
  registerCanvasCommentTools(server, api, run);
26454
26740
  registerCanvasMediaTools(server, { api, cfg, run, edit, seen, placeFor });
26455
26741
  registerCanvasLayoutTools(server, { api, run, edit });
26742
+ registerCanvasRunTools(server, api, run);
26456
26743
  }
26457
26744
 
26458
26745
  // src/version.ts
26459
26746
  import { createRequire } from "module";
26460
26747
  var require2 = createRequire(import.meta.url);
26461
26748
  function readVersion() {
26462
- if ("0.29.0") return "0.29.0";
26749
+ if ("0.31.0") return "0.31.0";
26463
26750
  try {
26464
26751
  const pkg = require2("../package.json");
26465
26752
  return pkg.version ?? "0.0.0-unknown";
@@ -26474,7 +26761,7 @@ var SERVER_INSTRUCTIONS = [
26474
26761
  "BePlus: tudo roda pela conta do usu\xE1rio (diamantes, limites e hist\xF3rico na plataforma).",
26475
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.',
26476
26763
  "Calls: search_calls acha, get_call traz a transcri\xE7\xE3o.",
26477
- "Canvas online (canvas_*): leia com canvas_read_board antes de editar e siga o estado lido. Cada edi\xE7\xE3o vai num passo s\xF3; se outra pessoa mexeu nos mesmos n\xF3s, a ferramenta recusa e pede releitura. As ferramentas canvas_* n\xE3o geram m\xEDdia nem gastam diamantes: quem roda os n\xF3s \xE9 o usu\xE1rio no Canvas. D\xEA nome (label) aos n\xF3s, n\xE3o invente tipos nem portas, e confirme antes de canvas_delete.",
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.",
26478
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.',
26479
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.'
26480
26767
  ].join("\n");