beplus-mcp 0.37.1 → 0.40.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 +2 -1
- package/dist/index.js +304 -105
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,7 +89,8 @@ Reinicie o cliente. Rode a tool **`whoami`** para confirmar o vínculo.
|
|
|
89
89
|
| `list_custom_voices` | Lista suas vozes próprias do TTS 3.8 (máx. 3) com o `voice_xxx` para usar em `voice`. Clonar a própria voz só no app web. Somente leitura. |
|
|
90
90
|
| `design_voice` | Cria uma voz própria a partir de uma descrição. Gasta 💎 e leva ~30 s. |
|
|
91
91
|
| `delete_custom_voice` | Apaga uma voz própria (destrutivo, sem volta). |
|
|
92
|
-
| `generate_music` | Gera música completa (Suno v5
|
|
92
|
+
| `generate_music` | Gera música completa (Suno v6 / v6-wild / v6-mini; v5-5 e v4-5 são nomes legados) — modo descrição ou letra própria, tags, instrumental, vocal_gender, duration, auto_lyrics e task_type (extend, cover, upload_extend, upload_cover, sample). Devolve clip_id por faixa. Async. |
|
|
93
|
+
| `music_tool` | Ferramentas por faixa de uma música pronta: wav, midi, stems-basic, stems-full, bpm, lyrics-timeline. Consome diamantes; repetir não cobra. |
|
|
93
94
|
| `check_generation` | Status de uma geração async por id. |
|
|
94
95
|
| `cancel_generation` | Cancela uma geração em fila/processamento (estorna diamantes). |
|
|
95
96
|
| `estimate_cost` | Custo em diamantes antes de gerar. |
|
package/dist/index.js
CHANGED
|
@@ -21634,6 +21634,23 @@ var BeplusClient = class {
|
|
|
21634
21634
|
deleteCustomVoice(id) {
|
|
21635
21635
|
return this.request("DELETE", `/tts/custom-voices/${encodeURIComponent(id)}`, { timeoutMs: 3e4 });
|
|
21636
21636
|
}
|
|
21637
|
+
// request() já desembrulha `result`; o estado vem em `{ state }`.
|
|
21638
|
+
async startMusicTool(generationId, body2) {
|
|
21639
|
+
const r = await this.request(
|
|
21640
|
+
"POST",
|
|
21641
|
+
`/generations/${encodeURIComponent(generationId)}/music-tools`,
|
|
21642
|
+
{ body: body2, timeoutMs: 6e4 }
|
|
21643
|
+
);
|
|
21644
|
+
return r?.state ?? null;
|
|
21645
|
+
}
|
|
21646
|
+
async getMusicTool(generationId, clipId, tool) {
|
|
21647
|
+
const r = await this.request(
|
|
21648
|
+
"GET",
|
|
21649
|
+
`/generations/${encodeURIComponent(generationId)}/music-tools`,
|
|
21650
|
+
{ query: { clip_id: clipId, tool }, timeoutMs: 2e4 }
|
|
21651
|
+
);
|
|
21652
|
+
return r?.state ?? null;
|
|
21653
|
+
}
|
|
21637
21654
|
models() {
|
|
21638
21655
|
return this.request("GET", "/models");
|
|
21639
21656
|
}
|
|
@@ -22145,18 +22162,21 @@ async function buildImageBlocks(urls, cfg) {
|
|
|
22145
22162
|
|
|
22146
22163
|
// src/schemas/enums.ts
|
|
22147
22164
|
var IMAGE_MODELS = [
|
|
22165
|
+
"google/nano-banana-2.1",
|
|
22148
22166
|
"google/nano-banana-2",
|
|
22149
22167
|
"google/nano-banana-pro",
|
|
22150
22168
|
"openai/gpt-image-2",
|
|
22151
22169
|
"openai/gpt-image-1",
|
|
22152
22170
|
"bytedance/seedream-5.0-lite",
|
|
22153
22171
|
"bytedance/seedream-5.0-pro",
|
|
22172
|
+
"bytedance/seedream-5.0-pro-layers",
|
|
22154
22173
|
"midjourney"
|
|
22155
22174
|
];
|
|
22156
22175
|
var SEEDREAM_LITE_SIZES = ["2K", "3K", "4K"];
|
|
22157
22176
|
var SEEDREAM_PRO_SIZES = ["1K", "1.5K", "2K"];
|
|
22158
22177
|
var SEEDREAM_PRO_PIXELS = { min: 921600, max: 4624220 };
|
|
22159
22178
|
var SEEDREAM_PRO_MAX_IMAGES = 4;
|
|
22179
|
+
var SEEDREAM_LAYERS_SIZES = ["auto", "1K", "1.5K", "2K"];
|
|
22160
22180
|
var VIDEO_MODELS = [
|
|
22161
22181
|
"bytedance/seedance-2.5",
|
|
22162
22182
|
"bytedance/seedance-2.0",
|
|
@@ -22187,9 +22207,14 @@ var UPSCALE_VIDEO_VARIANTS = [
|
|
|
22187
22207
|
"Starlight Fast 2"
|
|
22188
22208
|
];
|
|
22189
22209
|
var MUSIC_MODELS = [
|
|
22210
|
+
"suno/v6",
|
|
22211
|
+
"suno/v6-wild",
|
|
22212
|
+
"suno/v6-mini",
|
|
22190
22213
|
"suno/v5-5",
|
|
22191
22214
|
"suno/v4-5"
|
|
22192
22215
|
];
|
|
22216
|
+
var MUSIC_TASK_TYPES = ["create", "extend", "cover", "upload_extend", "upload_cover", "sample"];
|
|
22217
|
+
var MUSIC_TOOLS = ["wav", "midi", "stems-basic", "stems-full", "bpm", "lyrics-timeline"];
|
|
22193
22218
|
var VOCAL_GENDERS = ["f", "m"];
|
|
22194
22219
|
var TTS_MODELS = [
|
|
22195
22220
|
"gemini-3.8-flash-lite-tts",
|
|
@@ -22237,7 +22262,7 @@ var VIDEO_ASPECT_RATIOS = [...ASPECT_RATIOS, "adaptive"];
|
|
|
22237
22262
|
var IMAGE_RESOLUTIONS = ["1K", "2K", "4K"];
|
|
22238
22263
|
var VIDEO_RESOLUTIONS = ["480p", "720p", "1080p", "4K"];
|
|
22239
22264
|
var DEFAULTS = {
|
|
22240
|
-
imageModel: "google/nano-banana-2",
|
|
22265
|
+
imageModel: "google/nano-banana-2.1",
|
|
22241
22266
|
imageAspect: "1:1",
|
|
22242
22267
|
imageResolution: "1K",
|
|
22243
22268
|
videoModel: "bytedance/seedance-2.0-fast",
|
|
@@ -22246,9 +22271,254 @@ var DEFAULTS = {
|
|
|
22246
22271
|
videoDuration: 5,
|
|
22247
22272
|
ttsModel: "gemini-3.8-flash-lite-tts",
|
|
22248
22273
|
voice: "Kore",
|
|
22249
|
-
musicModel: "suno/
|
|
22274
|
+
musicModel: "suno/v6"
|
|
22250
22275
|
};
|
|
22251
22276
|
|
|
22277
|
+
// src/tools/music.ts
|
|
22278
|
+
var MUSIC_BUDGET_MS = 15e4;
|
|
22279
|
+
var TOOL_POLL_MS = 5e3;
|
|
22280
|
+
var TOOL_BUDGET_MS = 24e4;
|
|
22281
|
+
var sleep2 = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
22282
|
+
function validateMusicArgs(a) {
|
|
22283
|
+
const type = a.task_type ?? "create";
|
|
22284
|
+
const customMode = a.custom_mode ?? false;
|
|
22285
|
+
if (a.title && a.title.trim().length > 79) return "`title` aceita no m\xE1ximo 79 caracteres.";
|
|
22286
|
+
if (a.auto_lyrics && !customMode) {
|
|
22287
|
+
return "`auto_lyrics` s\xF3 vale no modo letra (custom_mode=true). No modo descri\xE7\xE3o a IA j\xE1 escreve a letra.";
|
|
22288
|
+
}
|
|
22289
|
+
if (customMode && !a.lyrics?.trim() && !(a.auto_lyrics && a.description?.trim())) {
|
|
22290
|
+
return a.auto_lyrics ? "Com `auto_lyrics`, passe o tema em `lyrics` (ou `description`)." : "Modo letra (custom_mode=true) exige `lyrics`.";
|
|
22291
|
+
}
|
|
22292
|
+
if (!customMode && !a.description?.trim()) return "Modo descri\xE7\xE3o (custom_mode=false) exige `description`.";
|
|
22293
|
+
const needsSource = type === "extend" || type === "cover";
|
|
22294
|
+
const needsReference = type === "upload_extend" || type === "upload_cover" || type === "sample";
|
|
22295
|
+
const needsContinueAt = type === "extend" || type === "upload_extend";
|
|
22296
|
+
const usesWeight = type === "cover" || type === "upload_cover" || type === "sample";
|
|
22297
|
+
if (needsSource) {
|
|
22298
|
+
if (!a.source_generation_id?.trim() || !a.source_clip_id?.trim()) {
|
|
22299
|
+
return `task_type=${type} exige \`source_generation_id\` e \`source_clip_id\` (o clip_id sai em generate_music/check_generation da gera\xE7\xE3o original).`;
|
|
22300
|
+
}
|
|
22301
|
+
} else if (a.source_generation_id || a.source_clip_id) {
|
|
22302
|
+
return "`source_generation_id` e `source_clip_id` s\xF3 valem com task_type=extend ou cover.";
|
|
22303
|
+
}
|
|
22304
|
+
if (needsReference) {
|
|
22305
|
+
if (!a.reference_audio_url?.trim()) return `task_type=${type} exige \`reference_audio_url\` (URL ou caminho local do \xE1udio).`;
|
|
22306
|
+
} else if (a.reference_audio_url) {
|
|
22307
|
+
return "`reference_audio_url` s\xF3 vale com task_type=upload_extend, upload_cover ou sample.";
|
|
22308
|
+
}
|
|
22309
|
+
if (needsContinueAt) {
|
|
22310
|
+
if (a.continue_at == null) return `task_type=${type} exige \`continue_at\` (segundo em que a m\xFAsica continua).`;
|
|
22311
|
+
} else if (a.continue_at != null) {
|
|
22312
|
+
return "`continue_at` s\xF3 vale com task_type=extend ou upload_extend.";
|
|
22313
|
+
}
|
|
22314
|
+
if (a.audio_weight != null && !usesWeight) {
|
|
22315
|
+
return "`audio_weight` s\xF3 vale com task_type=cover, upload_cover ou sample.";
|
|
22316
|
+
}
|
|
22317
|
+
if (type === "sample") {
|
|
22318
|
+
if (a.sample_start_s == null || a.sample_end_s == null) return "task_type=sample exige `sample_start_s` e `sample_end_s`.";
|
|
22319
|
+
if (a.sample_end_s <= a.sample_start_s) return "`sample_end_s` precisa ser maior que `sample_start_s`.";
|
|
22320
|
+
if (!customMode && a.description && a.description.trim().length > 200) {
|
|
22321
|
+
return "No task_type=sample a `description` aceita no m\xE1ximo 200 caracteres.";
|
|
22322
|
+
}
|
|
22323
|
+
if (a.tags && a.tags.trim().length > 200) return "No task_type=sample as `tags` aceitam no m\xE1ximo 200 caracteres.";
|
|
22324
|
+
} else if (a.sample_start_s != null || a.sample_end_s != null) {
|
|
22325
|
+
return "`sample_start_s` e `sample_end_s` s\xF3 valem com task_type=sample.";
|
|
22326
|
+
}
|
|
22327
|
+
return null;
|
|
22328
|
+
}
|
|
22329
|
+
async function getMusicClips(client, generationId, status) {
|
|
22330
|
+
try {
|
|
22331
|
+
const st = status ?? await client.getStatus(generationId);
|
|
22332
|
+
return Array.isArray(st.music_clips) ? st.music_clips : [];
|
|
22333
|
+
} catch {
|
|
22334
|
+
return [];
|
|
22335
|
+
}
|
|
22336
|
+
}
|
|
22337
|
+
function formatMusicClips(generationId, urls, clips) {
|
|
22338
|
+
const lines = (urls.length ? urls : clips.map((c) => c.audio_url ?? "")).map((u, i) => {
|
|
22339
|
+
const clip = clips[i];
|
|
22340
|
+
return `${i + 1}. ${u || "(sem URL)"}${clip?.clip_id ? `
|
|
22341
|
+
clip_id: ${clip.clip_id}` : ""}`;
|
|
22342
|
+
});
|
|
22343
|
+
const chain = clips.length ? `
|
|
22344
|
+
Para estender/cobrir ou usar music_tool: generation_id=${generationId} + o clip_id da faixa.` : "";
|
|
22345
|
+
return `${lines.join("\n") || "(sem URLs)"}${chain}`;
|
|
22346
|
+
}
|
|
22347
|
+
function registerMusicTools(server, client, cfg) {
|
|
22348
|
+
server.registerTool(
|
|
22349
|
+
"generate_music",
|
|
22350
|
+
{
|
|
22351
|
+
title: "Gerar m\xFAsica",
|
|
22352
|
+
description: 'Gera uma m\xFAsica completa (com vocais ou instrumental) usando o Suno no IA Lab da BePlus. ASS\xCDNCRONO e LENTO (geralmente 30s\u20132+ min). Aguarda at\xE9 ~2,5 min; se n\xE3o terminar, retorna o generation_id e o status \u2014 ent\xE3o use check_generation. Consome diamantes. DOIS modos: (a) DESCRI\xC7\xC3O (custom_mode=false, padr\xE3o) \u2192 passe `description` ("uma balada triste de piano"); (b) LETRA PR\xD3PRIA (custom_mode=true) \u2192 passe `lyrics` (a letra) e opcionalmente `title`. Use `tags` para g\xEAnero/estilo em ambos os modos (ex.: "sertanejo, animado, vocal feminino"). `vocal_gender` (f/m) vale para todos os modelos e \xE9 ignorado quando `make_instrumental=true`. Cada gera\xE7\xE3o produz 2 faixas, cada uma com `clip_id`. `duration` (10 a 360s) \xE9 s\xF3 uma meta aproximada; com letra, a letra manda. `task_type` muda a opera\xE7\xE3o: create (padr\xE3o), extend (continuar uma faixa sua, exige source_generation_id + source_clip_id + continue_at), cover (nova vers\xE3o de uma faixa sua, exige source_generation_id + source_clip_id), upload_extend / upload_cover (partem de um \xE1udio seu em `reference_audio_url`, URL ou caminho local) e sample (usa um trecho do \xE1udio, exige reference_audio_url + sample_start_s + sample_end_s). Extend e upload_extend custam o pre\xE7o "suno/extend"; o resto, o pre\xE7o do modelo. Depois de pronta, use music_tool para WAV, MIDI, stems, BPM e letra sincronizada de uma faixa.' + TEAM_GATING_NOTE,
|
|
22353
|
+
inputSchema: {
|
|
22354
|
+
model: external_exports.enum(MUSIC_MODELS).default(DEFAULTS.musicModel).describe(
|
|
22355
|
+
"suno/v6 = padr\xE3o, polido e previs\xEDvel; suno/v6-wild = o mesmo, de prop\xF3sito menos previs\xEDvel; suno/v6-mini = menor e mais r\xE1pido. suno/v5-5 e suno/v4-5 s\xE3o nomes legados, hoje atendidos pelo v6."
|
|
22356
|
+
),
|
|
22357
|
+
custom_mode: external_exports.boolean().optional().describe("false (padr\xE3o) = gera a partir de `description`; true = usa sua `lyrics` pr\xF3pria."),
|
|
22358
|
+
description: external_exports.string().max(2500).optional().describe("Modo descri\xE7\xE3o (custom_mode=false): descreva a m\xFAsica desejada. Obrigat\xF3rio nesse modo."),
|
|
22359
|
+
lyrics: external_exports.string().max(5e3).optional().describe("Modo letra (custom_mode=true): a letra completa da m\xFAsica. Obrigat\xF3rio nesse modo."),
|
|
22360
|
+
title: external_exports.string().max(79).optional().describe("T\xEDtulo da m\xFAsica (modo letra). M\xE1ximo 79 caracteres."),
|
|
22361
|
+
tags: external_exports.string().max(1e3).optional().describe('G\xEAnero/estilo, separados por v\xEDrgula (ex.: "pop, energ\xE9tico, vocal masculino").'),
|
|
22362
|
+
make_instrumental: external_exports.boolean().optional().describe("true = sem vocais (apenas instrumental). Padr\xE3o false."),
|
|
22363
|
+
vocal_gender: external_exports.enum(VOCAL_GENDERS).optional().describe("G\xEAnero do vocal: f (feminino) ou m (masculino). Vale para todos os modelos; ignorado se instrumental."),
|
|
22364
|
+
negative_tags: external_exports.string().max(1e3).optional().describe("Estilos/elementos a evitar."),
|
|
22365
|
+
style_weight: external_exports.number().min(0).max(1).optional().describe("Peso do estilo (0\u20131). Maior = segue mais as tags. Omita para o modelo decidir."),
|
|
22366
|
+
weirdness_constraint: external_exports.number().min(0).max(1).optional().describe("Criatividade/imprevisibilidade (0\u20131). Omita para o modelo decidir."),
|
|
22367
|
+
duration: external_exports.number().int().min(10).max(360).optional().describe("Dura\xE7\xE3o-alvo aproximada em segundos (10 a 360). Com `lyrics`, a letra domina o tamanho."),
|
|
22368
|
+
auto_lyrics: external_exports.boolean().optional().describe("S\xF3 no modo letra (custom_mode=true): a IA escreve letra, t\xEDtulo e tags usando `lyrics` como TEMA."),
|
|
22369
|
+
task_type: external_exports.enum(MUSIC_TASK_TYPES).optional().describe("create (padr\xE3o) | extend | cover | upload_extend | upload_cover | sample. Veja a descri\xE7\xE3o da ferramenta."),
|
|
22370
|
+
source_generation_id: external_exports.string().min(1).optional().describe("extend/cover: id da gera\xE7\xE3o de m\xFAsica original."),
|
|
22371
|
+
source_clip_id: external_exports.string().min(1).optional().describe("extend/cover: clip_id da faixa original (aparece no resultado de generate_music / check_generation)."),
|
|
22372
|
+
continue_at: external_exports.number().min(0).optional().describe("extend/upload_extend: segundo da faixa a partir do qual continuar."),
|
|
22373
|
+
audio_weight: external_exports.number().min(0).max(1).optional().describe("cover/upload_cover/sample: fidelidade ao \xE1udio original (0 a 1)."),
|
|
22374
|
+
reference_audio_url: external_exports.string().min(1).optional().describe("upload_extend/upload_cover/sample: \xE1udio de origem. URL p\xFAblica ou caminho local (sobe para o storage da BePlus; outros hosts s\xE3o recusados)."),
|
|
22375
|
+
sample_start_s: external_exports.number().min(0).optional().describe("sample: in\xEDcio do trecho, em segundos."),
|
|
22376
|
+
sample_end_s: external_exports.number().min(0).optional().describe("sample: fim do trecho, em segundos (maior que o in\xEDcio)."),
|
|
22377
|
+
project: external_exports.string().max(64).optional().describe('Projeto (uuid OU code curto, ex.: "VRAO-26"). S\xF3 omita se o usu\xE1rio J\xC1 confirmou o projeto ativo nesta sess\xE3o.'),
|
|
22378
|
+
billing: external_exports.enum(["personal"]).optional().describe(
|
|
22379
|
+
'"personal" = cobra do seu diamante pessoal, IGNORANDO o projeto ativo (mesmo se houver um). Use quando quiser garantir que isto N\xC3O sai do budget de nenhum projeto. Incompat\xEDvel com `project`.'
|
|
22380
|
+
)
|
|
22381
|
+
}
|
|
22382
|
+
},
|
|
22383
|
+
async (args) => {
|
|
22384
|
+
try {
|
|
22385
|
+
const customMode = args.custom_mode ?? false;
|
|
22386
|
+
const invalid = validateMusicArgs(args);
|
|
22387
|
+
if (invalid) return errorResult(invalid);
|
|
22388
|
+
const input = {
|
|
22389
|
+
custom_mode: customMode,
|
|
22390
|
+
make_instrumental: args.make_instrumental ?? false
|
|
22391
|
+
};
|
|
22392
|
+
if (args.vocal_gender) input.vocal_gender = args.vocal_gender;
|
|
22393
|
+
if (args.negative_tags?.trim()) input.negative_tags = args.negative_tags.trim();
|
|
22394
|
+
if (args.style_weight != null) input.style_weight = args.style_weight;
|
|
22395
|
+
if (args.weirdness_constraint != null) input.weirdness_constraint = args.weirdness_constraint;
|
|
22396
|
+
if (args.tags?.trim()) input.tags = args.tags.trim();
|
|
22397
|
+
if (args.duration != null) input.duration = args.duration;
|
|
22398
|
+
if (args.auto_lyrics != null) input.auto_lyrics = args.auto_lyrics;
|
|
22399
|
+
if (args.task_type) input.task_type = args.task_type;
|
|
22400
|
+
if (args.source_generation_id) input.source_generation_id = args.source_generation_id.trim();
|
|
22401
|
+
if (args.source_clip_id) input.source_clip_id = args.source_clip_id.trim();
|
|
22402
|
+
if (args.continue_at != null) input.continue_at = args.continue_at;
|
|
22403
|
+
if (args.audio_weight != null) input.audio_weight = args.audio_weight;
|
|
22404
|
+
if (args.sample_start_s != null) input.sample_start_s = args.sample_start_s;
|
|
22405
|
+
if (args.sample_end_s != null) input.sample_end_s = args.sample_end_s;
|
|
22406
|
+
if (args.reference_audio_url) input.reference_audio_url = await resolveRef(cfg, args.reference_audio_url.trim());
|
|
22407
|
+
if (customMode) {
|
|
22408
|
+
input.prompt = args.lyrics?.trim() || args.description.trim();
|
|
22409
|
+
if (args.title?.trim()) input.title = args.title.trim();
|
|
22410
|
+
} else {
|
|
22411
|
+
const desc = args.description.trim();
|
|
22412
|
+
input.gpt_description_prompt = desc;
|
|
22413
|
+
input.prompt = desc;
|
|
22414
|
+
}
|
|
22415
|
+
const start = await client.startGeneration(
|
|
22416
|
+
compact({
|
|
22417
|
+
model: args.model,
|
|
22418
|
+
input,
|
|
22419
|
+
project: resolveProjectArg(args.project, args.billing, cfg.activeProject),
|
|
22420
|
+
billing: args.billing
|
|
22421
|
+
})
|
|
22422
|
+
);
|
|
22423
|
+
if (!start.success || !start.generation_id) {
|
|
22424
|
+
if (isTeamGatingError(start.message)) return errorResult(await buildPickProjectMessage(client));
|
|
22425
|
+
return errorResult(start.message || "Falha ao iniciar a gera\xE7\xE3o.");
|
|
22426
|
+
}
|
|
22427
|
+
const footer = costFooter(start, cfg);
|
|
22428
|
+
const { settled, status } = await pollUntilDone(client, start.generation_id, MUSIC_BUDGET_MS);
|
|
22429
|
+
if (!settled) {
|
|
22430
|
+
return textResult(
|
|
22431
|
+
`\u23F3 M\xFAsica ainda em produ\xE7\xE3o (id: ${start.generation_id}, ${status.progress ?? 0}%). Suno costuma levar alguns minutos \u2014 use check_generation com esse id.
|
|
22432
|
+
${footer}`
|
|
22433
|
+
);
|
|
22434
|
+
}
|
|
22435
|
+
if (status.status === "failed") {
|
|
22436
|
+
return errorResult(`Gera\xE7\xE3o falhou: ${status.error_message || "erro desconhecido"}.
|
|
22437
|
+
${footer}`);
|
|
22438
|
+
}
|
|
22439
|
+
const urls = outputUrls(status);
|
|
22440
|
+
const clips = await getMusicClips(client, start.generation_id, status);
|
|
22441
|
+
const list = formatMusicClips(start.generation_id, urls, clips);
|
|
22442
|
+
return textResult(`\u2705 M\xFAsica gerada (${args.model}), generation_id: ${start.generation_id}.
|
|
22443
|
+
${list}
|
|
22444
|
+
${footer}`);
|
|
22445
|
+
} catch (e) {
|
|
22446
|
+
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
22447
|
+
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
22448
|
+
if (isBudgetBlockedError(msg)) {
|
|
22449
|
+
return errorResult(`${apiErrorToUserMessage(e)}
|
|
22450
|
+
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
22451
|
+
}
|
|
22452
|
+
return errorResult(apiErrorToUserMessage(e));
|
|
22453
|
+
}
|
|
22454
|
+
}
|
|
22455
|
+
);
|
|
22456
|
+
server.registerTool(
|
|
22457
|
+
"music_tool",
|
|
22458
|
+
{
|
|
22459
|
+
title: "Ferramenta de faixa de m\xFAsica",
|
|
22460
|
+
description: "Roda uma ferramenta numa faixa de m\xFAsica Suno j\xE1 pronta: wav (2 cr\xE9ditos AIMusic), midi (10), stems-basic (20, 4 stems: vocal e instrumental), stems-full (50, at\xE9 24 stems), bpm (1) e lyrics-timeline (1, palavras com tempo). Consome diamantes (o valor vem do backend); pedir de novo uma ferramenta j\xE1 conclu\xEDda devolve o resultado salvo sem cobrar. Aguarda at\xE9 ~4 min. Informe generation_id e a faixa por `clip_id` ou `clip_index` (1 ou 2).",
|
|
22461
|
+
inputSchema: {
|
|
22462
|
+
generation_id: external_exports.string().min(1).describe("Id da gera\xE7\xE3o de m\xFAsica conclu\xEDda."),
|
|
22463
|
+
tool: external_exports.enum(MUSIC_TOOLS).describe("wav | midi | stems-basic | stems-full | bpm | lyrics-timeline."),
|
|
22464
|
+
clip_id: external_exports.string().min(1).optional().describe("clip_id da faixa (veja generate_music / check_generation)."),
|
|
22465
|
+
clip_index: external_exports.number().int().min(1).max(2).optional().describe("Alternativa ao clip_id: 1 ou 2."),
|
|
22466
|
+
full_words: external_exports.boolean().optional().describe("lyrics-timeline: true devolve todas as palavras com tempo. Padr\xE3o: s\xF3 um resumo.")
|
|
22467
|
+
}
|
|
22468
|
+
},
|
|
22469
|
+
async (args) => {
|
|
22470
|
+
try {
|
|
22471
|
+
let clipId = args.clip_id?.trim();
|
|
22472
|
+
if (!clipId) {
|
|
22473
|
+
if (!args.clip_index) return errorResult("Informe `clip_id` ou `clip_index` (1 ou 2).");
|
|
22474
|
+
const clips = await getMusicClips(client, args.generation_id);
|
|
22475
|
+
clipId = clips[args.clip_index - 1]?.clip_id;
|
|
22476
|
+
if (!clipId) return errorResult("N\xE3o achei o clip_id dessa faixa. Passe `clip_id` explicitamente.");
|
|
22477
|
+
}
|
|
22478
|
+
const tool = args.tool;
|
|
22479
|
+
let state = await client.startMusicTool(args.generation_id, { clip_id: clipId, tool });
|
|
22480
|
+
const started = Date.now();
|
|
22481
|
+
while (state?.status === "processing" && Date.now() - started < TOOL_BUDGET_MS) {
|
|
22482
|
+
await sleep2(TOOL_POLL_MS);
|
|
22483
|
+
state = await client.getMusicTool(args.generation_id, clipId, tool) ?? state;
|
|
22484
|
+
}
|
|
22485
|
+
if (!state) return errorResult("O backend n\xE3o devolveu o estado da ferramenta.");
|
|
22486
|
+
if (state.status === "failed") {
|
|
22487
|
+
return errorResult(`${tool} falhou: ${state.error || "erro desconhecido"}.`);
|
|
22488
|
+
}
|
|
22489
|
+
if (state.status === "processing") {
|
|
22490
|
+
return textResult(
|
|
22491
|
+
`\u23F3 ${tool} ainda processando (custo ${state.cost} \u{1F48E}). Chame music_tool de novo com os mesmos argumentos em instantes: n\xE3o cobra de novo.`
|
|
22492
|
+
);
|
|
22493
|
+
}
|
|
22494
|
+
return textResult(`\u2705 ${tool} pronto (${state.cost} \u{1F48E}).
|
|
22495
|
+
${formatToolResult(state, args.full_words ?? false)}`);
|
|
22496
|
+
} catch (e) {
|
|
22497
|
+
return errorResult(apiErrorToUserMessage(e));
|
|
22498
|
+
}
|
|
22499
|
+
}
|
|
22500
|
+
);
|
|
22501
|
+
}
|
|
22502
|
+
function formatToolResult(state, fullWords) {
|
|
22503
|
+
const r = state.result ?? {};
|
|
22504
|
+
const lines = [];
|
|
22505
|
+
if (r.url) lines.push(r.url);
|
|
22506
|
+
if (r.stems?.length) lines.push(...r.stems.map((s) => `${s.title}: ${s.url}`));
|
|
22507
|
+
if (r.bpm) lines.push(`BPM m\xE9dio ${r.bpm.avg} (m\xEDn ${r.bpm.min}, m\xE1x ${r.bpm.max})`);
|
|
22508
|
+
if (r.words?.length) {
|
|
22509
|
+
if (fullWords) {
|
|
22510
|
+
lines.push(r.words.map((w) => `${w.s.toFixed(2)}-${w.e.toFixed(2)} ${w.w}`).join("\n"));
|
|
22511
|
+
} else {
|
|
22512
|
+
const first = r.words.slice(0, 12).map((w) => w.w).join(" ");
|
|
22513
|
+
const end = r.words[r.words.length - 1].e;
|
|
22514
|
+
lines.push(
|
|
22515
|
+
`${r.words.length} palavras sincronizadas, at\xE9 ${end.toFixed(1)}s. In\xEDcio: "${first}". Use full_words=true para a lista completa.`
|
|
22516
|
+
);
|
|
22517
|
+
}
|
|
22518
|
+
}
|
|
22519
|
+
return lines.join("\n") || "(sem resultado)";
|
|
22520
|
+
}
|
|
22521
|
+
|
|
22252
22522
|
// src/tools/generation.ts
|
|
22253
22523
|
var IMAGE_BUDGET_MS = 9e4;
|
|
22254
22524
|
var VIDEO_BUDGET_MS = 12e4;
|
|
@@ -22281,9 +22551,11 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
22281
22551
|
// Sem teto local: quem conhece o limite de cada provedor é o backend, e
|
|
22282
22552
|
// ele responde com quanto reduzir. Um `.max()` aqui rejeitava prompts
|
|
22283
22553
|
// que o modelo aceita (Seedream/nano-banana não têm limite publicado).
|
|
22284
|
-
prompt: external_exports.string().
|
|
22554
|
+
prompt: external_exports.string().default("").describe(
|
|
22555
|
+
'Descri\xE7\xE3o da imagem desejada. No seedream-5.0-pro-layers \xE9 opcional: diga o que separar (ex.: "o produto, o texto e o fundo") ou deixe vazio para o modelo escolher.'
|
|
22556
|
+
),
|
|
22285
22557
|
model: external_exports.enum(IMAGE_MODELS).default(DEFAULTS.imageModel).describe(
|
|
22286
|
-
"nano-banana-2 = barato
|
|
22558
|
+
"nano-banana-2.1 = padr\xE3o, o mais novo e o mais barato do Google (metade do 2, texto mais n\xEDtido); nano-banana-2 = vers\xE3o anterior, mesmo comportamento; 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); seedream-5.0-pro-layers = N\xC3O gera do zero: separa UMA imagem (reference_images com 1 item) em base + at\xE9 16 camadas PNG transparentes do mesmo tamanho, prontas para empilhar ou virar PSD. Cobra o teto de 17 imagens e estorna as que n\xE3o vierem."
|
|
22287
22559
|
),
|
|
22288
22560
|
aspect_ratio: external_exports.enum(ASPECT_RATIOS).optional().describe(
|
|
22289
22561
|
"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."
|
|
@@ -22292,8 +22564,8 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
22292
22564
|
reference_images: external_exports.array(external_exports.string().min(1)).max(16).optional().describe(
|
|
22293
22565
|
"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)."
|
|
22294
22566
|
),
|
|
22295
|
-
google_search: external_exports.boolean().optional().describe("nano-banana-2: aterra a imagem em busca web do Google (fatos atuais). Padr\xE3o false."),
|
|
22296
|
-
image_search: external_exports.boolean().optional().describe("nano-banana-2: usa busca de imagens do Google como refer\xEAncia visual. Padr\xE3o false."),
|
|
22567
|
+
google_search: external_exports.boolean().optional().describe("nano-banana-2.1 e 2: aterra a imagem em busca web do Google (fatos atuais). Padr\xE3o false."),
|
|
22568
|
+
image_search: external_exports.boolean().optional().describe("nano-banana-2.1 e 2: usa busca de imagens do Google como refer\xEAncia visual. Padr\xE3o false."),
|
|
22297
22569
|
size: external_exports.union([
|
|
22298
22570
|
external_exports.enum(["1024x1024", "1536x1024", "1024x1536", "auto", "1K", "1.5K", "2K", "3K", "4K"]),
|
|
22299
22571
|
external_exports.string().regex(/^\d{3,5}x\d{3,5}$/, "use LARGURAxALTURA, ex.: 2048x1152")
|
|
@@ -22340,6 +22612,25 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
22340
22612
|
const isSeedreamLite = args.model === "bytedance/seedream-5.0-lite";
|
|
22341
22613
|
const isSeedreamPro = args.model === "bytedance/seedream-5.0-pro";
|
|
22342
22614
|
const isSeedream = isSeedreamLite || isSeedreamPro;
|
|
22615
|
+
const isSeedreamLayers = args.model === "bytedance/seedream-5.0-pro-layers";
|
|
22616
|
+
if (!isSeedreamLayers && !args.prompt.trim()) {
|
|
22617
|
+
return errorResult("prompt \xE9 obrigat\xF3rio (s\xF3 o seedream-5.0-pro-layers aceita sem prompt).");
|
|
22618
|
+
}
|
|
22619
|
+
if (isSeedreamLayers) {
|
|
22620
|
+
if (args.reference_images?.length !== 1) {
|
|
22621
|
+
return errorResult(
|
|
22622
|
+
"seedream-5.0-pro-layers separa UMA imagem em camadas: passe exatamente 1 item em reference_images."
|
|
22623
|
+
);
|
|
22624
|
+
}
|
|
22625
|
+
if (args.size && !SEEDREAM_LAYERS_SIZES.includes(args.size)) {
|
|
22626
|
+
return errorResult(
|
|
22627
|
+
`seedream-5.0-pro-layers aceita size ${SEEDREAM_LAYERS_SIZES.join("/")} (voc\xEA pediu ${args.size}).`
|
|
22628
|
+
);
|
|
22629
|
+
}
|
|
22630
|
+
if (args.num_images || args.batch_images) {
|
|
22631
|
+
return errorResult("seedream-5.0-pro-layers devolve sempre base + camadas; num_images e batch_images n\xE3o se aplicam.");
|
|
22632
|
+
}
|
|
22633
|
+
}
|
|
22343
22634
|
if (isSeedream) {
|
|
22344
22635
|
const allowed = isSeedreamPro ? SEEDREAM_PRO_SIZES : SEEDREAM_LITE_SIZES;
|
|
22345
22636
|
const free = args.size?.match(/^(\d+)x(\d+)$/);
|
|
@@ -22404,7 +22695,7 @@ function registerGenerationTools(server, client, cfg) {
|
|
|
22404
22695
|
reference_images,
|
|
22405
22696
|
google_search: args.google_search,
|
|
22406
22697
|
image_search: args.image_search,
|
|
22407
|
-
size: args.size ?? (isSeedream ? "2K" : void 0),
|
|
22698
|
+
size: args.size ?? (isSeedream ? "2K" : isSeedreamLayers ? "auto" : void 0),
|
|
22408
22699
|
// Série do Seedream Lite: o backend lê o par sequential_image_generation
|
|
22409
22700
|
// + max_images (nomes da API da BytePlus). `batch_images` é só o nome
|
|
22410
22701
|
// amigável exposto aqui.
|
|
@@ -22771,7 +23062,8 @@ ${BUDGET_BLOCKED_GUIDANCE}`);
|
|
|
22771
23062
|
if (status.status === "completed") {
|
|
22772
23063
|
const urls = outputUrls(status);
|
|
22773
23064
|
const blocks = await buildImageBlocks(urls, cfg);
|
|
22774
|
-
const
|
|
23065
|
+
const isAudio = urls.length > 0 && urls.every((u) => /\.(mp3|m4a|wav|ogg)(\?|$)/i.test(u));
|
|
23066
|
+
const list = isAudio ? formatMusicClips(generation_id, urls, await getMusicClips(client, generation_id, status)) : urls.map((u, i) => `${i + 1}. ${u}`).join("\n") || "(sem URLs)";
|
|
22775
23067
|
const draftProvider = status.seedance_provider ?? "byteplus";
|
|
22776
23068
|
const draftHint = status.draft_task_id ? `
|
|
22777
23069
|
|
|
@@ -23700,100 +23992,6 @@ Use o valor de "voice" no generate_audio com um modelo 3.8 (sem multi_speaker).`
|
|
|
23700
23992
|
);
|
|
23701
23993
|
}
|
|
23702
23994
|
|
|
23703
|
-
// src/tools/music.ts
|
|
23704
|
-
var MUSIC_BUDGET_MS = 15e4;
|
|
23705
|
-
function registerMusicTools(server, client, cfg) {
|
|
23706
|
-
server.registerTool(
|
|
23707
|
-
"generate_music",
|
|
23708
|
-
{
|
|
23709
|
-
title: "Gerar m\xFAsica",
|
|
23710
|
-
description: 'Gera uma m\xFAsica completa (com vocais ou instrumental) usando o Suno no IA Lab da BePlus. ASS\xCDNCRONO e LENTO (geralmente 30s\u20132+ min). Aguarda at\xE9 ~2,5 min; se n\xE3o terminar, retorna o generation_id e o status \u2014 ent\xE3o use check_generation. Consome diamantes. DOIS modos: (a) DESCRI\xC7\xC3O (custom_mode=false, padr\xE3o) \u2192 passe `description` ("uma balada triste de piano"); (b) LETRA PR\xD3PRIA (custom_mode=true) \u2192 passe `lyrics` (a letra) e opcionalmente `title`. Use `tags` para g\xEAnero/estilo em ambos os modos (ex.: "sertanejo, animado, vocal feminino"). `vocal_gender` (f/m) s\xF3 \xE9 aceito pelo Suno v4.5 e \xE9 ignorado quando `make_instrumental=true`.' + TEAM_GATING_NOTE,
|
|
23711
|
-
inputSchema: {
|
|
23712
|
-
model: external_exports.enum(MUSIC_MODELS).default(DEFAULTS.musicModel).describe("suno/v5-5 = mais novo; suno/v4-5 = r\xE1pido/consistente (\xFAnico que aceita vocal_gender)."),
|
|
23713
|
-
custom_mode: external_exports.boolean().optional().describe("false (padr\xE3o) = gera a partir de `description`; true = usa sua `lyrics` pr\xF3pria."),
|
|
23714
|
-
description: external_exports.string().max(2500).optional().describe("Modo descri\xE7\xE3o (custom_mode=false): descreva a m\xFAsica desejada. Obrigat\xF3rio nesse modo."),
|
|
23715
|
-
lyrics: external_exports.string().max(5e3).optional().describe("Modo letra (custom_mode=true): a letra completa da m\xFAsica. Obrigat\xF3rio nesse modo."),
|
|
23716
|
-
title: external_exports.string().max(120).optional().describe("T\xEDtulo da m\xFAsica (modo letra)."),
|
|
23717
|
-
tags: external_exports.string().max(1e3).optional().describe('G\xEAnero/estilo, separados por v\xEDrgula (ex.: "pop, energ\xE9tico, vocal masculino").'),
|
|
23718
|
-
make_instrumental: external_exports.boolean().optional().describe("true = sem vocais (apenas instrumental). Padr\xE3o false."),
|
|
23719
|
-
vocal_gender: external_exports.enum(VOCAL_GENDERS).optional().describe("G\xEAnero do vocal: f (feminino) ou m (masculino). S\xF3 Suno v4.5; ignorado se instrumental."),
|
|
23720
|
-
negative_tags: external_exports.string().max(1e3).optional().describe("Estilos/elementos a evitar."),
|
|
23721
|
-
style_weight: external_exports.number().min(0).max(1).optional().describe("Peso do estilo (0\u20131). Maior = segue mais as tags. Omita para o modelo decidir."),
|
|
23722
|
-
weirdness_constraint: external_exports.number().min(0).max(1).optional().describe("Criatividade/imprevisibilidade (0\u20131). Omita para o modelo decidir."),
|
|
23723
|
-
project: external_exports.string().max(64).optional().describe('Projeto (uuid OU code curto, ex.: "VRAO-26"). S\xF3 omita se o usu\xE1rio J\xC1 confirmou o projeto ativo nesta sess\xE3o.'),
|
|
23724
|
-
billing: external_exports.enum(["personal"]).optional().describe(
|
|
23725
|
-
'"personal" = cobra do seu diamante pessoal, IGNORANDO o projeto ativo (mesmo se houver um). Use quando quiser garantir que isto N\xC3O sai do budget de nenhum projeto. Incompat\xEDvel com `project`.'
|
|
23726
|
-
)
|
|
23727
|
-
}
|
|
23728
|
-
},
|
|
23729
|
-
async (args) => {
|
|
23730
|
-
try {
|
|
23731
|
-
const customMode = args.custom_mode ?? false;
|
|
23732
|
-
if (customMode && !args.lyrics?.trim()) {
|
|
23733
|
-
return errorResult("Modo letra (custom_mode=true) exige `lyrics`.");
|
|
23734
|
-
}
|
|
23735
|
-
if (!customMode && !args.description?.trim()) {
|
|
23736
|
-
return errorResult("Modo descri\xE7\xE3o (custom_mode=false) exige `description`.");
|
|
23737
|
-
}
|
|
23738
|
-
const input = {
|
|
23739
|
-
custom_mode: customMode,
|
|
23740
|
-
make_instrumental: args.make_instrumental ?? false
|
|
23741
|
-
};
|
|
23742
|
-
if (args.vocal_gender) input.vocal_gender = args.vocal_gender;
|
|
23743
|
-
if (args.negative_tags?.trim()) input.negative_tags = args.negative_tags.trim();
|
|
23744
|
-
if (args.style_weight != null) input.style_weight = args.style_weight;
|
|
23745
|
-
if (args.weirdness_constraint != null) input.weirdness_constraint = args.weirdness_constraint;
|
|
23746
|
-
if (args.tags?.trim()) input.tags = args.tags.trim();
|
|
23747
|
-
if (customMode) {
|
|
23748
|
-
input.prompt = args.lyrics.trim();
|
|
23749
|
-
if (args.title?.trim()) input.title = args.title.trim();
|
|
23750
|
-
} else {
|
|
23751
|
-
const desc = args.description.trim();
|
|
23752
|
-
input.gpt_description_prompt = desc;
|
|
23753
|
-
input.prompt = desc;
|
|
23754
|
-
}
|
|
23755
|
-
const start = await client.startGeneration(
|
|
23756
|
-
compact({
|
|
23757
|
-
model: args.model,
|
|
23758
|
-
input,
|
|
23759
|
-
project: resolveProjectArg(args.project, args.billing, cfg.activeProject),
|
|
23760
|
-
billing: args.billing
|
|
23761
|
-
})
|
|
23762
|
-
);
|
|
23763
|
-
if (!start.success || !start.generation_id) {
|
|
23764
|
-
if (isTeamGatingError(start.message)) return errorResult(await buildPickProjectMessage(client));
|
|
23765
|
-
return errorResult(start.message || "Falha ao iniciar a gera\xE7\xE3o.");
|
|
23766
|
-
}
|
|
23767
|
-
const footer = costFooter(start, cfg);
|
|
23768
|
-
const { settled, status } = await pollUntilDone(client, start.generation_id, MUSIC_BUDGET_MS);
|
|
23769
|
-
if (!settled) {
|
|
23770
|
-
return textResult(
|
|
23771
|
-
`\u23F3 M\xFAsica ainda em produ\xE7\xE3o (id: ${start.generation_id}, ${status.progress ?? 0}%). Suno costuma levar alguns minutos \u2014 use check_generation com esse id.
|
|
23772
|
-
${footer}`
|
|
23773
|
-
);
|
|
23774
|
-
}
|
|
23775
|
-
if (status.status === "failed") {
|
|
23776
|
-
return errorResult(`Gera\xE7\xE3o falhou: ${status.error_message || "erro desconhecido"}.
|
|
23777
|
-
${footer}`);
|
|
23778
|
-
}
|
|
23779
|
-
const urls = outputUrls(status);
|
|
23780
|
-
const list = urls.map((u, i) => `${i + 1}. ${u}`).join("\n") || "(sem URLs)";
|
|
23781
|
-
return textResult(`\u2705 M\xFAsica gerada (${args.model}).
|
|
23782
|
-
${list}
|
|
23783
|
-
${footer}`);
|
|
23784
|
-
} catch (e) {
|
|
23785
|
-
const msg = e instanceof ApiError ? e.message : e instanceof Error ? e.message : "";
|
|
23786
|
-
if (isTeamGatingError(msg)) return errorResult(await buildPickProjectMessage(client));
|
|
23787
|
-
if (isBudgetBlockedError(msg)) {
|
|
23788
|
-
return errorResult(`${apiErrorToUserMessage(e)}
|
|
23789
|
-
${BUDGET_BLOCKED_GUIDANCE}`);
|
|
23790
|
-
}
|
|
23791
|
-
return errorResult(apiErrorToUserMessage(e));
|
|
23792
|
-
}
|
|
23793
|
-
}
|
|
23794
|
-
);
|
|
23795
|
-
}
|
|
23796
|
-
|
|
23797
23995
|
// src/tools/account.ts
|
|
23798
23996
|
function registerAccountTools(server, client, _cfg) {
|
|
23799
23997
|
server.registerTool(
|
|
@@ -23883,6 +24081,7 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
|
|
|
23883
24081
|
quality: external_exports.string().optional(),
|
|
23884
24082
|
aspect_ratio: external_exports.string().optional().describe("Seedream Pro: a propor\xE7\xE3o muda os pixels e a faixa de pre\xE7o."),
|
|
23885
24083
|
num_images: external_exports.number().int().min(1).optional().describe("Seedream Pro: imagens por pedido; cada uma \xE9 cobrada."),
|
|
24084
|
+
task_type: external_exports.string().optional().describe('[m\xFAsica] extend e upload_extend usam o pre\xE7o "suno/extend"; os demais, o do modelo.'),
|
|
23886
24085
|
provider: external_exports.enum(["byteplus", "fal"]).optional().describe("Seedance: provedor que vai gerar (padr\xE3o byteplus). `fal` tem pre\xE7o pr\xF3prio, mais caro."),
|
|
23887
24086
|
// Upscale: o preço não sai de uma chave fixa — vem das medidas do
|
|
23888
24087
|
// arquivo (megapixel de saída na imagem, bloco de 10s no vídeo). Sem
|
|
@@ -23919,7 +24118,7 @@ Limites simult\xE2neos por usu\xE1rio: imagem=${m.limits.perUserMaxImage}, v\xED
|
|
|
23919
24118
|
}
|
|
23920
24119
|
const c = await client.calculateCost(
|
|
23921
24120
|
compact({
|
|
23922
|
-
model: args.model,
|
|
24121
|
+
model: args.model.startsWith("suno/") && (args.task_type === "extend" || args.task_type === "upload_extend") ? "suno/extend" : args.model,
|
|
23923
24122
|
resolution: args.resolution,
|
|
23924
24123
|
duration: args.duration,
|
|
23925
24124
|
mode: args.mode,
|
|
@@ -24513,7 +24712,7 @@ var KINDS = {
|
|
|
24513
24712
|
music: {
|
|
24514
24713
|
title: "Trilha",
|
|
24515
24714
|
size: { w: 320, h: 212 },
|
|
24516
|
-
data: { model: "suno/
|
|
24715
|
+
data: { model: "suno/v6", instrumental: true, negativeTags: ["vocals", "choir", "whistling", "marching band", "epic orchestral"] },
|
|
24517
24716
|
inputs: ["prompt", "negative"],
|
|
24518
24717
|
outputs: ["audio", "approved"],
|
|
24519
24718
|
dynamic: "lyrics quando n\xE3o \xE9 instrumental"
|
|
@@ -27558,7 +27757,7 @@ N\xF3s: ${Object.entries(ids).map(([k, v]) => `${k}=${v}`).join(" \xB7 ")}` : ""
|
|
|
27558
27757
|
import { createRequire } from "module";
|
|
27559
27758
|
var require2 = createRequire(import.meta.url);
|
|
27560
27759
|
function readVersion() {
|
|
27561
|
-
if ("0.
|
|
27760
|
+
if ("0.40.0") return "0.40.0";
|
|
27562
27761
|
try {
|
|
27563
27762
|
const pkg = require2("../package.json");
|
|
27564
27763
|
return pkg.version ?? "0.0.0-unknown";
|