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/README.md +5 -1
- package/dist/index.js +374 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6145,11 +6145,11 @@ var require_format = __commonJS({
|
|
|
6145
6145
|
}
|
|
6146
6146
|
function getFormat(fmtDef) {
|
|
6147
6147
|
const code = fmtDef instanceof RegExp ? (0, codegen_1.regexpCode)(fmtDef) : opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(schema)}` : void 0;
|
|
6148
|
-
const
|
|
6148
|
+
const fmt3 = gen.scopeValue("formats", { key: schema, ref: fmtDef, code });
|
|
6149
6149
|
if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) {
|
|
6150
|
-
return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${
|
|
6150
|
+
return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${fmt3}.validate`];
|
|
6151
6151
|
}
|
|
6152
|
-
return ["string", fmtDef,
|
|
6152
|
+
return ["string", fmtDef, fmt3];
|
|
6153
6153
|
}
|
|
6154
6154
|
function validCondition() {
|
|
6155
6155
|
if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) {
|
|
@@ -6811,8 +6811,8 @@ var require_limit = __commonJS({
|
|
|
6811
6811
|
ref: self.formats,
|
|
6812
6812
|
code: opts.code.formats
|
|
6813
6813
|
});
|
|
6814
|
-
const
|
|
6815
|
-
cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${
|
|
6814
|
+
const fmt3 = gen.const("fmt", (0, codegen_1._)`${fmts}[${fCxt.schemaCode}]`);
|
|
6815
|
+
cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt3} != "object"`, (0, codegen_1._)`${fmt3} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt3}.compare != "function"`, compareCode(fmt3)));
|
|
6816
6816
|
}
|
|
6817
6817
|
function validateFormat() {
|
|
6818
6818
|
const format = fCxt.schema;
|
|
@@ -6822,15 +6822,15 @@ var require_limit = __commonJS({
|
|
|
6822
6822
|
if (typeof fmtDef != "object" || fmtDef instanceof RegExp || typeof fmtDef.compare != "function") {
|
|
6823
6823
|
throw new Error(`"${keyword}": format "${format}" does not define "compare" function`);
|
|
6824
6824
|
}
|
|
6825
|
-
const
|
|
6825
|
+
const fmt3 = gen.scopeValue("formats", {
|
|
6826
6826
|
key: format,
|
|
6827
6827
|
ref: fmtDef,
|
|
6828
6828
|
code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format)}` : void 0
|
|
6829
6829
|
});
|
|
6830
|
-
cxt.fail$data(compareCode(
|
|
6830
|
+
cxt.fail$data(compareCode(fmt3));
|
|
6831
6831
|
}
|
|
6832
|
-
function compareCode(
|
|
6833
|
-
return (0, codegen_1._)`${
|
|
6832
|
+
function compareCode(fmt3) {
|
|
6833
|
+
return (0, codegen_1._)`${fmt3}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`;
|
|
6834
6834
|
}
|
|
6835
6835
|
},
|
|
6836
6836
|
dependencies: ["format"]
|
|
@@ -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.
|
|
22276
|
-
"
|
|
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
|
-
|
|
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
|
|
22991
|
-
if (
|
|
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 (
|
|
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 (
|
|
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(
|
|
24098
|
-
return this.req("GET", `/boards/${
|
|
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(
|
|
24134
|
+
async opsAfter(boardId4, after) {
|
|
24102
24135
|
try {
|
|
24103
|
-
const { envelopes } = await this.req("GET", `/boards/${
|
|
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(
|
|
24113
|
-
const send = () => this.req("POST", `/boards/${
|
|
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(
|
|
24135
|
-
return this.req("GET", `/boards/${
|
|
24167
|
+
members(boardId4) {
|
|
24168
|
+
return this.req("GET", `/boards/${boardId4}/members`);
|
|
24136
24169
|
}
|
|
24137
|
-
putMember(
|
|
24138
|
-
return this.req("PUT", `/boards/${
|
|
24170
|
+
putMember(boardId4, userId, role) {
|
|
24171
|
+
return this.req("PUT", `/boards/${boardId4}/members/${userId}`, { body: { role } });
|
|
24139
24172
|
}
|
|
24140
|
-
removeMember(
|
|
24141
|
-
return this.req("DELETE", `/boards/${
|
|
24173
|
+
removeMember(boardId4, userId) {
|
|
24174
|
+
return this.req("DELETE", `/boards/${boardId4}/members/${userId}`);
|
|
24142
24175
|
}
|
|
24143
24176
|
// ── comentários (academy-backend #313) ────────────────────────────────────
|
|
24144
|
-
listComments(
|
|
24145
|
-
return this.req("GET", `/boards/${
|
|
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(
|
|
24172
|
-
return this.write("POST", `/boards/${
|
|
24204
|
+
createComment(boardId4, body2) {
|
|
24205
|
+
return this.write("POST", `/boards/${boardId4}/comments`, body2);
|
|
24173
24206
|
}
|
|
24174
|
-
resolveComment(
|
|
24175
|
-
return this.write("POST", `/boards/${
|
|
24207
|
+
resolveComment(boardId4, commentId2) {
|
|
24208
|
+
return this.write("POST", `/boards/${boardId4}/comments/${commentId2}/resolve`);
|
|
24176
24209
|
}
|
|
24177
|
-
reopenComment(
|
|
24178
|
-
return this.write("POST", `/boards/${
|
|
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(
|
|
24455
|
-
return { id: randomUUID(), boardId:
|
|
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,
|
|
24554
|
+
function removeNodeOps(doc, nodeId2) {
|
|
24511
24555
|
const ops = [];
|
|
24512
|
-
const edgeIds = Object.keys(doc.edges).filter((id) => doc.edges[id].source ===
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
24918
|
-
const node = doc.nodes[
|
|
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,
|
|
24943
|
-
const ids = doc.nodeHistory?.[
|
|
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[
|
|
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,
|
|
25419
|
-
const node = doc.nodes[
|
|
25420
|
-
if (!node) throw new CanvasToolError(`N\xF3 ${
|
|
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 ${
|
|
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,
|
|
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,
|
|
25481
|
+
out.libraryItemId = libraryItemId(u, nodeId2);
|
|
25438
25482
|
}
|
|
25439
25483
|
return JSON.stringify(out);
|
|
25440
25484
|
}
|
|
25441
|
-
function sequenceHint(u,
|
|
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" + (
|
|
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
|
|
25502
|
-
let
|
|
25545
|
+
const boardId4 = args.board_id;
|
|
25546
|
+
let nodeId2 = args.node_id;
|
|
25503
25547
|
let done;
|
|
25504
25548
|
try {
|
|
25505
|
-
done = await edit(
|
|
25549
|
+
done = await edit(boardId4, async (doc) => {
|
|
25506
25550
|
const item = mediaItem(u);
|
|
25507
|
-
if (
|
|
25508
|
-
const node = needMediaNode(doc,
|
|
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
|
-
|
|
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,
|
|
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
|
|
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,
|
|
25842
|
-
const node = doc.nodes[
|
|
25843
|
-
if (!node) throw new CanvasToolError(`N\xF3 ${
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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.
|
|
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
|
|
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");
|