beplus-mcp 0.26.0 → 0.27.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
@@ -916,10 +916,10 @@ var require_codegen = __commonJS({
916
916
  return this._leafNode(new Throw(error2));
917
917
  }
918
918
  // start self-balancing block
919
- block(body, nodeCount) {
919
+ block(body2, nodeCount) {
920
920
  this._blockStarts.push(this._nodes.length);
921
- if (body)
922
- this.code(body).endBlock(nodeCount);
921
+ if (body2)
922
+ this.code(body2).endBlock(nodeCount);
923
923
  return this;
924
924
  }
925
925
  // end the current self-balancing block
@@ -2344,15 +2344,15 @@ var require_validate = __commonJS({
2344
2344
  validateFunction(it, () => (0, boolSchema_1.topBoolOrEmptySchema)(it));
2345
2345
  }
2346
2346
  exports.validateFunctionCode = validateFunctionCode;
2347
- function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body) {
2347
+ function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body2) {
2348
2348
  if (opts.code.es5) {
2349
2349
  gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => {
2350
2350
  gen.code((0, codegen_1._)`"use strict"; ${funcSourceUrl(schema, opts)}`);
2351
2351
  destructureValCxtES5(gen, opts);
2352
- gen.code(body);
2352
+ gen.code(body2);
2353
2353
  });
2354
2354
  } else {
2355
- gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body));
2355
+ gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body2));
2356
2356
  }
2357
2357
  }
2358
2358
  function destructureValCxt(opts) {
@@ -21156,11 +21156,11 @@ var EMPTY_COMPLETION_RESULT = {
21156
21156
  var ApiError = class extends Error {
21157
21157
  httpStatus;
21158
21158
  body;
21159
- constructor(httpStatus, message, body) {
21159
+ constructor(httpStatus, message, body2) {
21160
21160
  super(message);
21161
21161
  this.name = "ApiError";
21162
21162
  this.httpStatus = httpStatus;
21163
- this.body = body;
21163
+ this.body = body2;
21164
21164
  }
21165
21165
  };
21166
21166
  function apiErrorToUserMessage(err) {
@@ -21244,14 +21244,14 @@ var BeplusClient = class {
21244
21244
  }
21245
21245
  return json2 && typeof json2 === "object" && "result" in json2 ? json2.result : json2;
21246
21246
  }
21247
- startGeneration(body) {
21248
- return this.request("POST", "/generations", { body });
21247
+ startGeneration(body2) {
21248
+ return this.request("POST", "/generations", { body: body2 });
21249
21249
  }
21250
21250
  listProjects() {
21251
21251
  return this.request("GET", "/projects");
21252
21252
  }
21253
- createProject(body) {
21254
- return this.request("POST", "/projects", { body });
21253
+ createProject(body2) {
21254
+ return this.request("POST", "/projects", { body: body2 });
21255
21255
  }
21256
21256
  getActiveProject() {
21257
21257
  return this.request("GET", "/projects/active");
@@ -21263,8 +21263,8 @@ var BeplusClient = class {
21263
21263
  listClients() {
21264
21264
  return this.request("GET", "/clients");
21265
21265
  }
21266
- createClient(body) {
21267
- return this.request("POST", "/clients", { body });
21266
+ createClient(body2) {
21267
+ return this.request("POST", "/clients", { body: body2 });
21268
21268
  }
21269
21269
  setActiveClient(client) {
21270
21270
  return this.request("PUT", "/clients/active", { body: { client } });
@@ -21286,8 +21286,8 @@ var BeplusClient = class {
21286
21286
  }
21287
21287
  });
21288
21288
  }
21289
- tts(body) {
21290
- return this.request("POST", "/tts", { body, timeoutMs: 9e4 });
21289
+ tts(body2) {
21290
+ return this.request("POST", "/tts", { body: body2, timeoutMs: 9e4 });
21291
21291
  }
21292
21292
  models() {
21293
21293
  return this.request("GET", "/models");
@@ -21302,11 +21302,11 @@ var BeplusClient = class {
21302
21302
  me(billing) {
21303
21303
  return this.request("GET", "/me", { query: { billing } });
21304
21304
  }
21305
- readProvenance(body) {
21306
- return this.request("POST", "/read-provenance", { body, timeoutMs: 3e4 });
21305
+ readProvenance(body2) {
21306
+ return this.request("POST", "/read-provenance", { body: body2, timeoutMs: 3e4 });
21307
21307
  }
21308
- analyzeMedia(body) {
21309
- return this.request("POST", "/analyze-media", { body, timeoutMs: 3e5 });
21308
+ analyzeMedia(body2) {
21309
+ return this.request("POST", "/analyze-media", { body: body2, timeoutMs: 3e5 });
21310
21310
  }
21311
21311
  searchCalls(query) {
21312
21312
  return this.request("GET", "/calls", {
@@ -22874,7 +22874,7 @@ function registerAudioTools(server, client, cfg) {
22874
22874
  },
22875
22875
  async (args) => {
22876
22876
  try {
22877
- const body = compact({
22877
+ const body2 = compact({
22878
22878
  text: args.text,
22879
22879
  voice: args.voice,
22880
22880
  model: args.model,
@@ -22882,7 +22882,7 @@ function registerAudioTools(server, client, cfg) {
22882
22882
  multiSpeaker: args.multi_speaker,
22883
22883
  project: args.project ?? cfg.activeProject ?? void 0
22884
22884
  });
22885
- const res = await client.tts(body);
22885
+ const res = await client.tts(body2);
22886
22886
  const parts = [`Custo: ${res.diamond_cost} \u{1F48E}`];
22887
22887
  if (res.diamonds_refunded) parts.push(`Estorno: ${res.diamonds_refunded} \u{1F48E}`);
22888
22888
  const projLine = res.project_code ? `
@@ -23378,11 +23378,11 @@ function fmtDate(iso) {
23378
23378
  function callLine(c) {
23379
23379
  const company = c.company_name ? ` \xB7 ${c.company_name}` : "";
23380
23380
  const proj = c.project_name ? ` \xB7 ${c.client_name ? c.client_name + " / " : ""}${c.project_name}` : "";
23381
- const who = c.speakers.length ? ` \xB7 ${c.speakers.join(", ")}` : c.participant_count ? ` \xB7 ${c.participant_count} falantes` : "";
23381
+ const who2 = c.speakers.length ? ` \xB7 ${c.speakers.join(", ")}` : c.participant_count ? ` \xB7 ${c.participant_count} falantes` : "";
23382
23382
  const vis = VIS_LABEL[c.visibility] ?? c.visibility;
23383
23383
  const flag = c.status !== "ready" ? ` [${c.status}]` : "";
23384
23384
  return `\u2022 ${c.title ?? "(sem t\xEDtulo)"}${flag}
23385
- ${fmtDate(c.date)} \xB7 ${fmtDuration(c.duration_sec)} \xB7 ${vis}${company}${proj}${who}
23385
+ ${fmtDate(c.date)} \xB7 ${fmtDuration(c.duration_sec)} \xB7 ${vis}${company}${proj}${who2}
23386
23386
  id: ${c.id}`;
23387
23387
  }
23388
23388
  function renderCall(c) {
@@ -23475,6 +23475,12 @@ function errorRaw(err) {
23475
23475
  const raw = err.body?.error?.raw;
23476
23476
  return raw && typeof raw === "object" ? raw : void 0;
23477
23477
  }
23478
+ var commentPacing = {
23479
+ perMinute: 50,
23480
+ retryWaitsMs: [5e3, 15e3, 45e3],
23481
+ now: () => Date.now(),
23482
+ sleep: (ms) => new Promise((r) => setTimeout(r, Math.max(0, ms)))
23483
+ };
23478
23484
  var CanvasApi = class {
23479
23485
  constructor(client, cfg) {
23480
23486
  this.client = client;
@@ -23484,6 +23490,8 @@ var CanvasApi = class {
23484
23490
  cfg;
23485
23491
  coreVersion = PINNED_CORE_VERSION;
23486
23492
  userId = null;
23493
+ /** Instantes das escritas de comentário no último minuto. */
23494
+ writes = [];
23487
23495
  req(method, path, opts = {}) {
23488
23496
  return this.client.request(method, path, { ...opts, base: this.cfg.canvasBase });
23489
23497
  }
@@ -23499,16 +23507,16 @@ var CanvasApi = class {
23499
23507
  listBoards(kind = "board") {
23500
23508
  return this.req("GET", "/boards", { query: { kind } });
23501
23509
  }
23502
- createBoard(body) {
23503
- return this.req("POST", "/boards", { body });
23510
+ createBoard(body2) {
23511
+ return this.req("POST", "/boards", { body: body2 });
23504
23512
  }
23505
- snapshot(boardId2) {
23506
- return this.req("GET", `/boards/${boardId2}`);
23513
+ snapshot(boardId3) {
23514
+ return this.req("GET", `/boards/${boardId3}`);
23507
23515
  }
23508
23516
  /** Replay depois de `after`. `null` quando o servidor manda reler a foto (410). */
23509
- async opsAfter(boardId2, after) {
23517
+ async opsAfter(boardId3, after) {
23510
23518
  try {
23511
- const { envelopes } = await this.req("GET", `/boards/${boardId2}/ops`, {
23519
+ const { envelopes } = await this.req("GET", `/boards/${boardId3}/ops`, {
23512
23520
  query: { after }
23513
23521
  });
23514
23522
  return envelopes;
@@ -23517,8 +23525,8 @@ var CanvasApi = class {
23517
23525
  throw e;
23518
23526
  }
23519
23527
  }
23520
- async commit(boardId2, ops) {
23521
- const send = () => this.req("POST", `/boards/${boardId2}/ops`, {
23528
+ async commit(boardId3, ops) {
23529
+ const send = () => this.req("POST", `/boards/${boardId3}/ops`, {
23522
23530
  body: { coreVersion: this.coreVersion, ops }
23523
23531
  });
23524
23532
  try {
@@ -23539,14 +23547,51 @@ var CanvasApi = class {
23539
23547
  }
23540
23548
  }
23541
23549
  }
23542
- members(boardId2) {
23543
- return this.req("GET", `/boards/${boardId2}/members`);
23550
+ members(boardId3) {
23551
+ return this.req("GET", `/boards/${boardId3}/members`);
23552
+ }
23553
+ putMember(boardId3, userId, role) {
23554
+ return this.req("PUT", `/boards/${boardId3}/members/${userId}`, { body: { role } });
23544
23555
  }
23545
- putMember(boardId2, userId, role) {
23546
- return this.req("PUT", `/boards/${boardId2}/members/${userId}`, { body: { role } });
23556
+ removeMember(boardId3, userId) {
23557
+ return this.req("DELETE", `/boards/${boardId3}/members/${userId}`);
23547
23558
  }
23548
- removeMember(boardId2, userId) {
23549
- return this.req("DELETE", `/boards/${boardId2}/members/${userId}`);
23559
+ // ── comentários (academy-backend #313) ────────────────────────────────────
23560
+ listComments(boardId3, status = "open") {
23561
+ return this.req("GET", `/boards/${boardId3}/comments`, { query: { status } });
23562
+ }
23563
+ /**
23564
+ * Escrita de comentário passa por aqui: o back limita a 60 por minuto por pessoa
23565
+ * (balde fixo, sem Retry-After). O MCP se espaça abaixo do teto e, se ainda
23566
+ * assim vier 429 (a pessoa também comentando no Canvas), espera e tenta de novo.
23567
+ */
23568
+ async write(method, path, body2) {
23569
+ const p = commentPacing;
23570
+ const now = p.now();
23571
+ this.writes = this.writes.filter((t) => now - t < 6e4);
23572
+ if (this.writes.length >= p.perMinute) {
23573
+ await p.sleep(this.writes[0] + 6e4 - now);
23574
+ this.writes.shift();
23575
+ }
23576
+ for (let attempt = 0; ; attempt++) {
23577
+ this.writes.push(p.now());
23578
+ try {
23579
+ return await this.req(method, path, body2 === void 0 ? {} : { body: body2 });
23580
+ } catch (e) {
23581
+ if (!(e instanceof ApiError) || e.httpStatus !== 429 || attempt >= p.retryWaitsMs.length) throw e;
23582
+ await p.sleep(p.retryWaitsMs[attempt]);
23583
+ }
23584
+ }
23585
+ }
23586
+ /** Raiz ou resposta. O back devolve sempre a THREAD DA RAIZ (resposta nova = última de `replies`). */
23587
+ createComment(boardId3, body2) {
23588
+ return this.write("POST", `/boards/${boardId3}/comments`, body2);
23589
+ }
23590
+ resolveComment(boardId3, commentId2) {
23591
+ return this.write("POST", `/boards/${boardId3}/comments/${commentId2}/resolve`);
23592
+ }
23593
+ reopenComment(boardId3, commentId2) {
23594
+ return this.write("POST", `/boards/${boardId3}/comments/${commentId2}/reopen`);
23550
23595
  }
23551
23596
  /** Só para os testes conferirem o aprendizado. */
23552
23597
  get currentCoreVersion() {
@@ -23563,6 +23608,8 @@ var CoreMismatchError = class extends Error {
23563
23608
  }
23564
23609
  serverVersion;
23565
23610
  };
23611
+ var CanvasToolError = class extends Error {
23612
+ };
23566
23613
 
23567
23614
  // src/canvas/catalog.ts
23568
23615
  var WORKFLOW_NODE_TYPE = "workflow";
@@ -23816,8 +23863,8 @@ function checkOp(op) {
23816
23863
  return null;
23817
23864
  }
23818
23865
  }
23819
- function envelope(boardId2, op, actorId) {
23820
- return { id: randomUUID(), boardId: boardId2, clientId: CLIENT_ID, actorId, seq: null, ts: Date.now(), op };
23866
+ function envelope(boardId3, op, actorId) {
23867
+ return { id: randomUUID(), boardId: boardId3, clientId: CLIENT_ID, actorId, seq: null, ts: Date.now(), op };
23821
23868
  }
23822
23869
  function checkEnvelope(env) {
23823
23870
  const bad = checkOp(env.op);
@@ -24067,6 +24114,289 @@ function touchedNodes(envelopes) {
24067
24114
  return out;
24068
24115
  }
24069
24116
 
24117
+ // src/canvas/comments.ts
24118
+ function mmss(t) {
24119
+ const total = Math.max(0, Math.floor(t));
24120
+ const h = Math.floor(total / 3600);
24121
+ const m = Math.floor(total % 3600 / 60);
24122
+ const s = total % 60;
24123
+ const pad = (n) => String(n).padStart(2, "0");
24124
+ return h ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
24125
+ }
24126
+ function nodeRef(doc, nodeId) {
24127
+ const node = doc.nodes[nodeId];
24128
+ const kind = nodeKind(node) ?? node.type;
24129
+ return `"${oneLine(nodeName(node), 60)}" (${kind}, ${node.id})`;
24130
+ }
24131
+ function hashString(text) {
24132
+ let h1 = 3735928559;
24133
+ let h2 = 1103547991;
24134
+ for (let i = 0; i < text.length; i += 1) {
24135
+ const ch = text.charCodeAt(i);
24136
+ h1 = Math.imul(h1 ^ ch, 2654435761);
24137
+ h2 = Math.imul(h2 ^ ch, 1597334677);
24138
+ }
24139
+ h1 = Math.imul(h1 ^ h1 >>> 16, 2246822507) ^ Math.imul(h2 ^ h2 >>> 13, 3266489909);
24140
+ h2 = Math.imul(h2 ^ h2 >>> 16, 2246822507) ^ Math.imul(h1 ^ h1 >>> 13, 3266489909);
24141
+ const hex = (n) => (n >>> 0).toString(16).padStart(8, "0");
24142
+ return hex(h2) + hex(h1);
24143
+ }
24144
+ function outputKeyOf(entry) {
24145
+ for (const field of [entry.resultId, entry.id, entry.generationId]) {
24146
+ if (typeof field === "string" && field !== "" && field.length <= 128) return field;
24147
+ }
24148
+ if (typeof entry.url === "string" && entry.url !== "") return `url-${hashString(entry.url)}`;
24149
+ return null;
24150
+ }
24151
+ function versionOf(doc, nodeId, outputKey) {
24152
+ const ids = doc.nodeHistory?.[nodeId];
24153
+ if (Array.isArray(ids)) {
24154
+ const i = ids.indexOf(outputKey);
24155
+ if (i >= 0) return i + 1;
24156
+ }
24157
+ const history = doc.nodes[nodeId]?.data.history;
24158
+ if (Array.isArray(history)) {
24159
+ const i = history.findIndex(
24160
+ (h) => typeof h === "object" && h !== null && !Array.isArray(h) && outputKeyOf(h) === outputKey
24161
+ );
24162
+ if (i >= 0) return history.length - i;
24163
+ }
24164
+ return null;
24165
+ }
24166
+ function describeAnchor(anchor, doc) {
24167
+ if (!anchor || anchor.kind === "point") return { text: "no board" };
24168
+ const exists = !!doc.nodes[anchor.nodeId];
24169
+ if (!exists) {
24170
+ return { text: `no n\xF3 ${anchor.nodeId}`, gone: "o n\xF3 foi apagado" };
24171
+ }
24172
+ const ref = nodeRef(doc, anchor.nodeId);
24173
+ switch (anchor.kind) {
24174
+ case "node":
24175
+ return { text: `no n\xF3 ${ref}`, nodeId: anchor.nodeId };
24176
+ case "output": {
24177
+ const v = versionOf(doc, anchor.nodeId, anchor.outputKey);
24178
+ return v === null ? { text: `numa vers\xE3o de ${ref} (${anchor.outputKey})`, nodeId: anchor.nodeId, gone: "essa vers\xE3o n\xE3o est\xE1 mais no hist\xF3rico do n\xF3" } : { text: `na vers\xE3o ${v} de ${ref}`, nodeId: anchor.nodeId };
24179
+ }
24180
+ case "timecode": {
24181
+ const node = doc.nodes[anchor.nodeId];
24182
+ const kind = nodeKind(node);
24183
+ const t = mmss(Number(anchor.t) || 0);
24184
+ if (kind === "montage") {
24185
+ const name = nodeName(node);
24186
+ const title = name === KINDS.montage.title ? "" : ` "${oneLine(name, 60)}"`;
24187
+ return { text: `em ${t} da Montagem${title} (${node.id})`, nodeId: anchor.nodeId };
24188
+ }
24189
+ return { text: `em ${t} de ${ref}`, nodeId: anchor.nodeId };
24190
+ }
24191
+ default:
24192
+ return { text: "no board" };
24193
+ }
24194
+ }
24195
+ function who(c) {
24196
+ return c.author?.name?.trim() || "algu\xE9m";
24197
+ }
24198
+ function reactionsText(r) {
24199
+ if (Array.isArray(r)) {
24200
+ const parts = r.map((x) => {
24201
+ const rec = x ?? {};
24202
+ const emoji2 = typeof rec.emoji === "string" ? rec.emoji : null;
24203
+ const n = typeof rec.count === "number" ? rec.count : Array.isArray(rec.userIds) ? rec.userIds.length : Array.isArray(rec.users) ? rec.users.length : 1;
24204
+ return emoji2 ? `${emoji2}${n > 1 ? `\xD7${n}` : ""}` : null;
24205
+ }).filter(Boolean);
24206
+ return parts.join(" ");
24207
+ }
24208
+ if (r && typeof r === "object") {
24209
+ return Object.entries(r).map(([emoji2, v]) => {
24210
+ const n = Array.isArray(v) ? v.length : typeof v === "number" ? v : 1;
24211
+ return `${emoji2}${n > 1 ? `\xD7${n}` : ""}`;
24212
+ }).join(" ");
24213
+ }
24214
+ return "";
24215
+ }
24216
+ var MAX_REPLIES = 5;
24217
+ function threadLines(t, doc) {
24218
+ const where = describeAnchor(t.anchor, doc);
24219
+ const state = t.resolvedAt ? " \xB7 RESOLVIDO" : "";
24220
+ const reacts = reactionsText(t.reactions);
24221
+ const lines = t.deleted ? [`- ${t.id} \xB7 ${where.text}${state}`, " (coment\xE1rio raiz apagado: n\xE3o d\xE1 para responder nem resolver)"] : [`- ${t.id} \xB7 ${where.text} \xB7 ${who(t)}${state}${reacts ? ` \xB7 ${reacts}` : ""}`, ` pedido: ${oneLine(t.body ?? "", 400)}`];
24222
+ if (where.gone && !t.deleted) lines.push(` \u26A0 ALVO SUMIU: ${where.gone}. Responda com pergunta, n\xE3o resolva.`);
24223
+ const replies = Array.isArray(t.replies) ? t.replies : [];
24224
+ for (const r of replies.slice(-MAX_REPLIES)) {
24225
+ lines.push(r.deleted ? " \u21B3 (resposta apagada)" : ` \u21B3 ${who(r)}: ${oneLine(r.body ?? "", 140)}`);
24226
+ }
24227
+ if (replies.length > MAX_REPLIES) lines.splice(lines.length - MAX_REPLIES, 0, ` \u21B3 \u2026 ${replies.length - MAX_REPLIES} resposta(s) antes`);
24228
+ return lines;
24229
+ }
24230
+ function commentsSummary(threads, doc, status) {
24231
+ const label = status === "resolved" ? "resolvido(s)" : status === "all" ? "no total" : "aberto(s)";
24232
+ if (!threads.length) return `Nenhum coment\xE1rio ${label === "no total" ? "no board" : label}.`;
24233
+ const out = [`${threads.length} coment\xE1rio(s) ${label}:`];
24234
+ for (const t of threads) out.push(...threadLines(t, doc));
24235
+ return out.join("\n");
24236
+ }
24237
+
24238
+ // src/tools/canvas-comments.ts
24239
+ var boardId = external_exports.string().uuid().describe("Id (uuid) do board.");
24240
+ var commentId = external_exports.string().min(1).max(128).describe("Id do coment\xE1rio raiz, como sai em canvas_list_comments.");
24241
+ var body = external_exports.string().trim().min(1).max(4e3);
24242
+ var mentions = external_exports.array(external_exports.string().uuid()).max(50).optional().describe("userIds a marcar (opcional; quem n\xE3o \xE9 membro \xE9 ignorado).");
24243
+ function registerCanvasCommentTools(server, api, run) {
24244
+ async function onComment(fn) {
24245
+ try {
24246
+ return await fn();
24247
+ } catch (e) {
24248
+ if (!(e instanceof ApiError)) throw e;
24249
+ if (e.httpStatus === 404) {
24250
+ throw new CanvasToolError("Coment\xE1rio (ou board) n\xE3o encontrado ou apagado. Liste de novo com canvas_list_comments.");
24251
+ }
24252
+ if (e.httpStatus === 400) throw new CanvasToolError(`O servidor recusou o coment\xE1rio: ${e.message}.`);
24253
+ if (e.httpStatus === 403 && (!api.usesPat || /ialab:/.test(e.message))) {
24254
+ throw new CanvasToolError(
24255
+ "O login pelo navegador deste MCP n\xE3o tem a permiss\xE3o ialab:spend, que toda escrita de coment\xE1rio exige. Rode `beplus-mcp login` de novo e autorize a permiss\xE3o de gastar/editar."
24256
+ );
24257
+ }
24258
+ if (e.httpStatus === 403) throw new CanvasToolError(`Sem permiss\xE3o: ${e.message}.`);
24259
+ if (e.httpStatus === 429) {
24260
+ throw new CanvasToolError(
24261
+ "Limite de escrita em coment\xE1rios (60 por minuto por pessoa) mesmo depois de esperar. Pare o lote, diga \xE0 pessoa quais j\xE1 foram fechados e siga daqui a 1 minuto a partir do pr\xF3ximo."
24262
+ );
24263
+ }
24264
+ throw e;
24265
+ }
24266
+ }
24267
+ const newReplyId = (thread) => {
24268
+ const last = thread?.replies?.at(-1);
24269
+ return last?.id ?? "?";
24270
+ };
24271
+ server.registerTool(
24272
+ "canvas_list_comments",
24273
+ {
24274
+ title: "Canvas: listar coment\xE1rios",
24275
+ description: "Lista os coment\xE1rios de um board (padr\xE3o: abertos): id, onde est\xE1 (n\xF3, vers\xE3o, minutagem da Montagem ou board), autor, pedido e respostas. Avisa quando o alvo sumiu. S\xF3 leitura.",
24276
+ inputSchema: {
24277
+ board_id: boardId,
24278
+ status: external_exports.enum(["open", "resolved", "all"]).optional().describe("open (padr\xE3o), resolved ou all.")
24279
+ },
24280
+ annotations: { readOnlyHint: true, openWorldHint: false }
24281
+ },
24282
+ async ({ board_id, status }) => run(async () => {
24283
+ const st = status ?? "open";
24284
+ const [snap, threads] = await Promise.all([api.snapshot(board_id), api.listComments(board_id, st)]);
24285
+ const text = commentsSummary(threads, snap.doc, st);
24286
+ const open2 = threads.some((t) => !t.resolvedAt);
24287
+ return open2 ? `${text}
24288
+
24289
+ Para aplicar: leia o n\xF3 com canvas_read_board (node_ids), edite pelas canvas_* e feche com canvas_resolve_comment dizendo o que fez.` : text;
24290
+ })
24291
+ );
24292
+ server.registerTool(
24293
+ "canvas_reply_comment",
24294
+ {
24295
+ title: "Canvas: responder coment\xE1rio",
24296
+ description: "Responde numa thread de coment\xE1rio sem resolver. Use para perguntar quando o pedido \xE9 amb\xEDguo ou o alvo sumiu.",
24297
+ inputSchema: { board_id: boardId, comment_id: commentId, body, mentions },
24298
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
24299
+ },
24300
+ async ({ board_id, comment_id, body: text, mentions: m }) => run(async () => {
24301
+ const thread = await onComment(
24302
+ () => api.createComment(board_id, { body: text, parentId: comment_id, ...m?.length ? { mentions: m } : {} })
24303
+ );
24304
+ const open2 = thread?.resolvedAt ? "A thread est\xE1 resolvida; reabra com canvas_reopen_comment se precisar." : "A thread segue aberta.";
24305
+ return `\u2705 Resposta ${newReplyId(thread)} em ${comment_id}: "${oneLine(text, 120)}". ${open2}`;
24306
+ })
24307
+ );
24308
+ server.registerTool(
24309
+ "canvas_resolve_comment",
24310
+ {
24311
+ title: "Canvas: resolver coment\xE1rio",
24312
+ description: "Fecha um coment\xE1rio depois de aplicar o ajuste. done \xE9 obrigat\xF3rio: uma frase curta dizendo o que foi feito (vira resposta na thread antes de resolver). N\xE3o resolva pedido amb\xEDguo nem com alvo sumido: responda com pergunta.",
24313
+ inputSchema: {
24314
+ board_id: boardId,
24315
+ comment_id: commentId,
24316
+ done: external_exports.string().trim().min(3).max(500).describe('O que foi feito, ex.: "troquei a fala do b2 e encurtei o b3".')
24317
+ },
24318
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
24319
+ },
24320
+ async ({ board_id, comment_id, done }) => run(async () => {
24321
+ const thread = await onComment(() => api.createComment(board_id, { body: done, parentId: comment_id }));
24322
+ const replyId = newReplyId(thread);
24323
+ try {
24324
+ await onComment(() => api.resolveComment(board_id, comment_id));
24325
+ } catch (e) {
24326
+ const why = e instanceof Error ? e.message : String(e);
24327
+ throw new CanvasToolError(
24328
+ `A resposta "${oneLine(done, 80)}" (${replyId}) foi postada em ${comment_id}, mas resolver falhou: ${why} N\xE3o repita canvas_resolve_comment (postaria a resposta de novo): avise a pessoa para resolver no Canvas.`
24329
+ );
24330
+ }
24331
+ return `\u2705 Resolvido ${comment_id}: "${oneLine(done, 120)}" (resposta ${replyId}).`;
24332
+ })
24333
+ );
24334
+ server.registerTool(
24335
+ "canvas_reopen_comment",
24336
+ {
24337
+ title: "Canvas: reabrir coment\xE1rio",
24338
+ description: "Reabre um coment\xE1rio resolvido. why (opcional) vira resposta na thread explicando o motivo.",
24339
+ inputSchema: { board_id: boardId, comment_id: commentId, why: external_exports.string().trim().min(1).max(1e3).optional() },
24340
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
24341
+ },
24342
+ async ({ board_id, comment_id, why }) => run(async () => {
24343
+ await onComment(() => api.reopenComment(board_id, comment_id));
24344
+ if (why) await onComment(() => api.createComment(board_id, { body: why, parentId: comment_id }));
24345
+ return `\u2705 Reaberto ${comment_id}${why ? ` com a resposta "${oneLine(why, 120)}"` : ""}.`;
24346
+ })
24347
+ );
24348
+ server.registerTool(
24349
+ "canvas_comment",
24350
+ {
24351
+ title: "Canvas: comentar",
24352
+ description: "Cria um coment\xE1rio novo (raiz), ancorado para aparecer no lugar certo do Canvas. Use para deixar uma pergunta ou observa\xE7\xE3o para a pessoa. \xC2ncora: node_id (no n\xF3); node_id + output_key (numa vers\xE3o gerada); node_id + t em segundos (minutagem da Montagem ou do v\xEDdeo); x,y (ponto do board); nada = acima do conte\xFAdo do board.",
24353
+ inputSchema: {
24354
+ board_id: boardId,
24355
+ body,
24356
+ node_id: external_exports.string().min(1).max(64).optional(),
24357
+ output_key: external_exports.string().min(1).max(128).optional().describe("Id da vers\xE3o (resultado) do n\xF3."),
24358
+ t: external_exports.number().finite().min(0).optional().describe("Segundos desde o in\xEDcio."),
24359
+ x: external_exports.number().finite().optional(),
24360
+ y: external_exports.number().finite().optional(),
24361
+ mentions
24362
+ },
24363
+ annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
24364
+ },
24365
+ async (args) => run(async () => {
24366
+ if (!args.node_id && (args.output_key !== void 0 || args.t !== void 0)) {
24367
+ throw new CanvasToolError("output_key e t precisam de node_id.");
24368
+ }
24369
+ if (args.output_key !== void 0 && args.t !== void 0) throw new CanvasToolError("Use output_key ou t, n\xE3o os dois.");
24370
+ if (args.x === void 0 !== (args.y === void 0)) throw new CanvasToolError("Informe x e y juntos.");
24371
+ const snap = await api.snapshot(args.board_id);
24372
+ let anchor;
24373
+ if (args.node_id) {
24374
+ if (!snap.doc.nodes[args.node_id]) {
24375
+ throw new CanvasToolError(`N\xF3 ${args.node_id} n\xE3o existe no board. Releia com canvas_read_board.`);
24376
+ }
24377
+ if (args.output_key !== void 0) anchor = { kind: "output", nodeId: args.node_id, outputKey: args.output_key };
24378
+ else if (args.t !== void 0) anchor = { kind: "timecode", nodeId: args.node_id, t: args.t };
24379
+ else anchor = { kind: "node", nodeId: args.node_id, dx: 0, dy: 0 };
24380
+ } else if (args.x !== void 0 && args.y !== void 0) {
24381
+ anchor = { kind: "point", x: args.x, y: args.y };
24382
+ } else {
24383
+ const nodes = Object.values(snap.doc.nodes);
24384
+ anchor = nodes.length ? { kind: "point", x: Math.min(...nodes.map((n) => n.position.x)), y: Math.min(...nodes.map((n) => n.position.y)) - 80 } : { kind: "point", x: 0, y: 0 };
24385
+ }
24386
+ const created = await onComment(
24387
+ () => api.createComment(args.board_id, {
24388
+ body: args.body,
24389
+ anchor,
24390
+ ...args.mentions?.length ? { mentions: args.mentions } : {}
24391
+ })
24392
+ );
24393
+ const where = describeAnchor(anchor, snap.doc).text;
24394
+ const at = anchor.kind === "point" && args.x !== void 0 ? ` em (${args.x}, ${args.y})` : "";
24395
+ return `\u2705 Coment\xE1rio ${created?.id ?? ""} criado ${where}${at}.`.replace(" ", " ");
24396
+ })
24397
+ );
24398
+ }
24399
+
24070
24400
  // src/tools/canvas.ts
24071
24401
  var REJECT_TEXT = {
24072
24402
  "node-missing": "o n\xF3 n\xE3o existe mais (algu\xE9m apagou)",
@@ -24076,9 +24406,7 @@ var REJECT_TEXT = {
24076
24406
  };
24077
24407
  var SECTION_Z = -1e3;
24078
24408
  var GAP = 80;
24079
- var CanvasToolError = class extends Error {
24080
- };
24081
- var boardId = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
24409
+ var boardId2 = external_exports.string().uuid().describe("Id (uuid) do board, como sai em canvas_list_boards.");
24082
24410
  var nodeIdArg = external_exports.string().min(1).max(128);
24083
24411
  var point = external_exports.object({ x: external_exports.number().finite(), y: external_exports.number().finite() });
24084
24412
  function registerCanvasTools(server, client, cfg) {
@@ -24222,7 +24550,7 @@ Aviso: ${changesSince(theirs, doc)}.`;
24222
24550
  title: "Canvas: ler board",
24223
24551
  description: "L\xEA um board: n\xF3s (id, tipo, nome, campos principais), liga\xE7\xF5es e v\xEDnculo com o IA Lab, sem m\xEDdia. Obrigat\xF3rio antes de editar (vale para a sess\xE3o). node_ids devolve o data completo desses n\xF3s.",
24224
24552
  inputSchema: {
24225
- board_id: boardId,
24553
+ board_id: boardId2,
24226
24554
  node_ids: external_exports.array(nodeIdArg).max(20).optional().describe("N\xF3s para ver o data inteiro (sem blobs).")
24227
24555
  },
24228
24556
  annotations: { readOnlyHint: true, openWorldHint: false }
@@ -24294,7 +24622,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24294
24622
  title: "Canvas: adicionar n\xF3",
24295
24623
  description: `Adiciona um n\xF3 (n\xE3o executa). 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.`,
24296
24624
  inputSchema: {
24297
- board_id: boardId,
24625
+ board_id: boardId2,
24298
24626
  kind: external_exports.string().min(1).max(40),
24299
24627
  label: external_exports.string().trim().min(1).max(120).optional().describe("Nome do cart\xE3o (data.label)."),
24300
24628
  data: external_exports.record(external_exports.unknown()).optional().describe("Campos do data. Ex.: narration {blocks:[{id,text}]}, text {text}, prompt {template}."),
@@ -24342,7 +24670,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24342
24670
  title: "Canvas: editar n\xF3",
24343
24671
  description: "Muda campos do data de um n\xF3, chave a chave (null apaga a chave), sem executar. label renomeia. N\xE3o troca o tipo. Leia o n\xF3 com node_ids antes para n\xE3o sobrescrever o que outra pessoa escreveu.",
24344
24672
  inputSchema: {
24345
- board_id: boardId,
24673
+ board_id: boardId2,
24346
24674
  node_id: nodeIdArg,
24347
24675
  data: external_exports.record(external_exports.unknown()).optional().describe("Chaves de data a gravar. Valor null apaga a chave."),
24348
24676
  label: external_exports.string().trim().min(1).max(120).optional(),
@@ -24379,7 +24707,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24379
24707
  title: "Canvas: ligar portas",
24380
24708
  description: "Liga a sa\xEDda de um n\xF3 \xE0 entrada de outro (ids e portas de canvas_read_board). Liga\xE7\xE3o repetida \xE9 ignorada.",
24381
24709
  inputSchema: {
24382
- board_id: boardId,
24710
+ board_id: boardId2,
24383
24711
  source: nodeIdArg,
24384
24712
  source_port: external_exports.string().min(1).max(80),
24385
24713
  target: nodeIdArg,
@@ -24406,7 +24734,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24406
24734
  title: "Canvas: apagar n\xF3s/liga\xE7\xF5es",
24407
24735
  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. N\xE3o apaga o board.",
24408
24736
  inputSchema: {
24409
- board_id: boardId,
24737
+ board_id: boardId2,
24410
24738
  node_ids: external_exports.array(nodeIdArg).max(200).optional(),
24411
24739
  edge_ids: external_exports.array(nodeIdArg).max(200).optional()
24412
24740
  },
@@ -24450,7 +24778,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24450
24778
  {
24451
24779
  title: "Canvas: renomear board",
24452
24780
  description: "Troca o t\xEDtulo do board (board.set). Precisa de papel de editor ou dono.",
24453
- inputSchema: { board_id: boardId, title: external_exports.string().trim().min(1).max(200) },
24781
+ inputSchema: { board_id: boardId2, title: external_exports.string().trim().min(1).max(200) },
24454
24782
  annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
24455
24783
  },
24456
24784
  async ({ board_id, title }) => run(
@@ -24466,7 +24794,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24466
24794
  {
24467
24795
  title: "Canvas: membros do board",
24468
24796
  description: "Lista quem tem acesso ao board (userId, nome, papel). S\xF3 leitura.",
24469
- inputSchema: { board_id: boardId },
24797
+ inputSchema: { board_id: boardId2 },
24470
24798
  annotations: { readOnlyHint: true, openWorldHint: false }
24471
24799
  },
24472
24800
  async ({ board_id }) => run(async () => {
@@ -24483,7 +24811,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24483
24811
  title: "Canvas: adicionar membro",
24484
24812
  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.",
24485
24813
  inputSchema: {
24486
- board_id: boardId,
24814
+ board_id: boardId2,
24487
24815
  user_id: external_exports.string().uuid(),
24488
24816
  role: external_exports.enum(["editor", "viewer"]).describe("editor edita; viewer s\xF3 v\xEA.")
24489
24817
  },
@@ -24499,7 +24827,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24499
24827
  {
24500
24828
  title: "Canvas: remover membro",
24501
24829
  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.",
24502
- inputSchema: { board_id: boardId, user_id: external_exports.string().uuid() },
24830
+ inputSchema: { board_id: boardId2, user_id: external_exports.string().uuid() },
24503
24831
  annotations: { readOnlyHint: false, destructiveHint: true, openWorldHint: false }
24504
24832
  },
24505
24833
  async ({ board_id, user_id }) => run(async () => {
@@ -24507,13 +24835,14 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
24507
24835
  return `\u2705 ${user_id} n\xE3o tem mais acesso ao board.`;
24508
24836
  })
24509
24837
  );
24838
+ registerCanvasCommentTools(server, api, run);
24510
24839
  }
24511
24840
 
24512
24841
  // src/version.ts
24513
24842
  import { createRequire } from "module";
24514
24843
  var require2 = createRequire(import.meta.url);
24515
24844
  function readVersion() {
24516
- if ("0.26.0") return "0.26.0";
24845
+ if ("0.27.0") return "0.27.0";
24517
24846
  try {
24518
24847
  const pkg = require2("../package.json");
24519
24848
  return pkg.version ?? "0.0.0-unknown";
@@ -24528,7 +24857,8 @@ var SERVER_INSTRUCTIONS = [
24528
24857
  "BePlus: tudo roda pela conta do usu\xE1rio (diamantes, limites e hist\xF3rico na plataforma).",
24529
24858
  '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.',
24530
24859
  "Calls: search_calls acha, get_call traz a transcri\xE7\xE3o.",
24531
- "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."
24860
+ "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.",
24861
+ '"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.'
24532
24862
  ].join("\n");
24533
24863
  function buildServer(cfg) {
24534
24864
  const server = new McpServer({ name: "beplus", version: VERSION }, { instructions: SERVER_INSTRUCTIONS });
@@ -24782,16 +25112,16 @@ async function json(url, init) {
24782
25112
  throw new Error(`Nao consegui falar com ${new URL(url).origin}. Confira a rede e o BEPLUS_API_URL.`);
24783
25113
  }
24784
25114
  const text = await res.text();
24785
- let body;
25115
+ let body2;
24786
25116
  try {
24787
- body = text ? JSON.parse(text) : void 0;
25117
+ body2 = text ? JSON.parse(text) : void 0;
24788
25118
  } catch {
24789
25119
  }
24790
25120
  if (!res.ok) {
24791
- const msg = body?.message ?? body?.error_description ?? body?.error ?? `HTTP ${res.status}`;
25121
+ const msg = body2?.message ?? body2?.error_description ?? body2?.error ?? `HTTP ${res.status}`;
24792
25122
  throw new Error(`${msg} (${url})`);
24793
25123
  }
24794
- return body;
25124
+ return body2;
24795
25125
  }
24796
25126
  async function discover(apiUrl2) {
24797
25127
  const meta = await json(`${apiUrl2}/.well-known/oauth-authorization-server`);