evomeme 0.1.10-beta.1 → 0.1.10-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -3237,8 +3237,8 @@ var require_utils = __commonJS({
3237
3237
  }
3238
3238
  return ind;
3239
3239
  }
3240
- function removeDotSegments(path59) {
3241
- let input = path59;
3240
+ function removeDotSegments(path60) {
3241
+ let input = path60;
3242
3242
  const output = [];
3243
3243
  let nextSlash = -1;
3244
3244
  let len = 0;
@@ -3490,8 +3490,8 @@ var require_schemes = __commonJS({
3490
3490
  wsComponent.secure = void 0;
3491
3491
  }
3492
3492
  if (wsComponent.resourceName) {
3493
- const [path59, query] = wsComponent.resourceName.split("?");
3494
- wsComponent.path = path59 && path59 !== "/" ? path59 : void 0;
3493
+ const [path60, query] = wsComponent.resourceName.split("?");
3494
+ wsComponent.path = path60 && path60 !== "/" ? path60 : void 0;
3495
3495
  wsComponent.query = query;
3496
3496
  wsComponent.resourceName = void 0;
3497
3497
  }
@@ -6909,7 +6909,7 @@ var require_windows = __commonJS({
6909
6909
  module.exports = isexe;
6910
6910
  isexe.sync = sync;
6911
6911
  var fs15 = __require("fs");
6912
- function checkPathExt(path59, options) {
6912
+ function checkPathExt(path60, options) {
6913
6913
  var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT;
6914
6914
  if (!pathext) {
6915
6915
  return true;
@@ -6920,25 +6920,25 @@ var require_windows = __commonJS({
6920
6920
  }
6921
6921
  for (var i = 0; i < pathext.length; i++) {
6922
6922
  var p = pathext[i].toLowerCase();
6923
- if (p && path59.substr(-p.length).toLowerCase() === p) {
6923
+ if (p && path60.substr(-p.length).toLowerCase() === p) {
6924
6924
  return true;
6925
6925
  }
6926
6926
  }
6927
6927
  return false;
6928
6928
  }
6929
- function checkStat(stat12, path59, options) {
6929
+ function checkStat(stat12, path60, options) {
6930
6930
  if (!stat12.isSymbolicLink() && !stat12.isFile()) {
6931
6931
  return false;
6932
6932
  }
6933
- return checkPathExt(path59, options);
6933
+ return checkPathExt(path60, options);
6934
6934
  }
6935
- function isexe(path59, options, cb) {
6936
- fs15.stat(path59, function(er, stat12) {
6937
- cb(er, er ? false : checkStat(stat12, path59, options));
6935
+ function isexe(path60, options, cb) {
6936
+ fs15.stat(path60, function(er, stat12) {
6937
+ cb(er, er ? false : checkStat(stat12, path60, options));
6938
6938
  });
6939
6939
  }
6940
- function sync(path59, options) {
6941
- return checkStat(fs15.statSync(path59), path59, options);
6940
+ function sync(path60, options) {
6941
+ return checkStat(fs15.statSync(path60), path60, options);
6942
6942
  }
6943
6943
  }
6944
6944
  });
@@ -6949,13 +6949,13 @@ var require_mode = __commonJS({
6949
6949
  module.exports = isexe;
6950
6950
  isexe.sync = sync;
6951
6951
  var fs15 = __require("fs");
6952
- function isexe(path59, options, cb) {
6953
- fs15.stat(path59, function(er, stat12) {
6952
+ function isexe(path60, options, cb) {
6953
+ fs15.stat(path60, function(er, stat12) {
6954
6954
  cb(er, er ? false : checkStat(stat12, options));
6955
6955
  });
6956
6956
  }
6957
- function sync(path59, options) {
6958
- return checkStat(fs15.statSync(path59), options);
6957
+ function sync(path60, options) {
6958
+ return checkStat(fs15.statSync(path60), options);
6959
6959
  }
6960
6960
  function checkStat(stat12, options) {
6961
6961
  return stat12.isFile() && checkMode(stat12, options);
@@ -6988,7 +6988,7 @@ var require_isexe = __commonJS({
6988
6988
  }
6989
6989
  module.exports = isexe;
6990
6990
  isexe.sync = sync;
6991
- function isexe(path59, options, cb) {
6991
+ function isexe(path60, options, cb) {
6992
6992
  if (typeof options === "function") {
6993
6993
  cb = options;
6994
6994
  options = {};
@@ -6998,7 +6998,7 @@ var require_isexe = __commonJS({
6998
6998
  throw new TypeError("callback not provided");
6999
6999
  }
7000
7000
  return new Promise(function(resolve, reject) {
7001
- isexe(path59, options || {}, function(er, is) {
7001
+ isexe(path60, options || {}, function(er, is) {
7002
7002
  if (er) {
7003
7003
  reject(er);
7004
7004
  } else {
@@ -7007,7 +7007,7 @@ var require_isexe = __commonJS({
7007
7007
  });
7008
7008
  });
7009
7009
  }
7010
- core(path59, options || {}, function(er, is) {
7010
+ core(path60, options || {}, function(er, is) {
7011
7011
  if (er) {
7012
7012
  if (er.code === "EACCES" || options && options.ignoreErrors) {
7013
7013
  er = null;
@@ -7017,9 +7017,9 @@ var require_isexe = __commonJS({
7017
7017
  cb(er, is);
7018
7018
  });
7019
7019
  }
7020
- function sync(path59, options) {
7020
+ function sync(path60, options) {
7021
7021
  try {
7022
- return core.sync(path59, options || {});
7022
+ return core.sync(path60, options || {});
7023
7023
  } catch (er) {
7024
7024
  if (options && options.ignoreErrors || er.code === "EACCES") {
7025
7025
  return false;
@@ -7035,7 +7035,7 @@ var require_isexe = __commonJS({
7035
7035
  var require_which = __commonJS({
7036
7036
  "node_modules/which/which.js"(exports, module) {
7037
7037
  var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys";
7038
- var path59 = __require("path");
7038
+ var path60 = __require("path");
7039
7039
  var COLON = isWindows ? ";" : ":";
7040
7040
  var isexe = require_isexe();
7041
7041
  var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" });
@@ -7073,7 +7073,7 @@ var require_which = __commonJS({
7073
7073
  return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd));
7074
7074
  const ppRaw = pathEnv[i];
7075
7075
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
7076
- const pCmd = path59.join(pathPart, cmd);
7076
+ const pCmd = path60.join(pathPart, cmd);
7077
7077
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
7078
7078
  resolve(subStep(p, i, 0));
7079
7079
  });
@@ -7100,7 +7100,7 @@ var require_which = __commonJS({
7100
7100
  for (let i = 0; i < pathEnv.length; i++) {
7101
7101
  const ppRaw = pathEnv[i];
7102
7102
  const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw;
7103
- const pCmd = path59.join(pathPart, cmd);
7103
+ const pCmd = path60.join(pathPart, cmd);
7104
7104
  const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd;
7105
7105
  for (let j = 0; j < pathExt.length; j++) {
7106
7106
  const cur = p + pathExt[j];
@@ -7148,7 +7148,7 @@ var require_path_key = __commonJS({
7148
7148
  var require_resolveCommand = __commonJS({
7149
7149
  "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) {
7150
7150
  "use strict";
7151
- var path59 = __require("path");
7151
+ var path60 = __require("path");
7152
7152
  var which = require_which();
7153
7153
  var getPathKey = require_path_key();
7154
7154
  function resolveCommandAttempt(parsed, withoutPathExt) {
@@ -7166,7 +7166,7 @@ var require_resolveCommand = __commonJS({
7166
7166
  try {
7167
7167
  resolved = which.sync(parsed.command, {
7168
7168
  path: env[getPathKey({ env })],
7169
- pathExt: withoutPathExt ? path59.delimiter : void 0
7169
+ pathExt: withoutPathExt ? path60.delimiter : void 0
7170
7170
  });
7171
7171
  } catch (e) {
7172
7172
  } finally {
@@ -7175,7 +7175,7 @@ var require_resolveCommand = __commonJS({
7175
7175
  }
7176
7176
  }
7177
7177
  if (resolved) {
7178
- resolved = path59.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
7178
+ resolved = path60.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved);
7179
7179
  }
7180
7180
  return resolved;
7181
7181
  }
@@ -7229,8 +7229,8 @@ var require_shebang_command = __commonJS({
7229
7229
  if (!match) {
7230
7230
  return null;
7231
7231
  }
7232
- const [path59, argument] = match[0].replace(/#! ?/, "").split(" ");
7233
- const binary = path59.split("/").pop();
7232
+ const [path60, argument] = match[0].replace(/#! ?/, "").split(" ");
7233
+ const binary = path60.split("/").pop();
7234
7234
  if (binary === "env") {
7235
7235
  return argument;
7236
7236
  }
@@ -7265,7 +7265,7 @@ var require_readShebang = __commonJS({
7265
7265
  var require_parse = __commonJS({
7266
7266
  "node_modules/cross-spawn/lib/parse.js"(exports, module) {
7267
7267
  "use strict";
7268
- var path59 = __require("path");
7268
+ var path60 = __require("path");
7269
7269
  var resolveCommand = require_resolveCommand();
7270
7270
  var escape2 = require_escape();
7271
7271
  var readShebang = require_readShebang();
@@ -7290,7 +7290,7 @@ var require_parse = __commonJS({
7290
7290
  const needsShell = !isExecutableRegExp.test(commandFile);
7291
7291
  if (parsed.options.forceShell || needsShell) {
7292
7292
  const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
7293
- parsed.command = path59.normalize(parsed.command);
7293
+ parsed.command = path60.normalize(parsed.command);
7294
7294
  parsed.command = escape2.command(parsed.command);
7295
7295
  parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars));
7296
7296
  const shellCommand = [parsed.command].concat(parsed.args).join(" ");
@@ -30414,6 +30414,7 @@ import path from "node:path";
30414
30414
 
30415
30415
  // src/language/common/en-US.ts
30416
30416
  var messages = {
30417
+ "error.project.missingBinding": "The workspace has no valid thread_id binding. Explicitly call evomeme_reinit_project to initialize it before submitting.",
30417
30418
  "auth.browserLogin": "Complete login in the browser that was opened. If it did not open, visit: ",
30418
30419
  "auth.loginSucceeded": "Login successful.",
30419
30420
  "auth.sessionExpired": "The login session expired or was denied. Start login again.",
@@ -30520,6 +30521,7 @@ var messages = {
30520
30521
  "update.remindRun": "Remind the user to run this command in a terminal:",
30521
30522
  "update.restartIde": "Restart the IDE after updating.",
30522
30523
  "error.network": "Network connection failed. Try again.",
30524
+ "error.network.timeoutPrefix": "Request timed out; limit (ms): ",
30523
30525
  "error.http.contextSeparator": ", ",
30524
30526
  "error.http.notFoundPrefix": "The server endpoint does not exist (",
30525
30527
  "error.http.notFoundSuffix": "). Verify that this endpoint is deployed and check the API URL and gateway routes.",
@@ -30961,6 +30963,7 @@ var en_US_default = messages;
30961
30963
 
30962
30964
  // src/language/common/zh-CN.ts
30963
30965
  var messages2 = {
30966
+ "error.project.missingBinding": "当前工作区缺少有效的 thread_id 绑定,请显式调用 evomeme_reinit_project 初始化工作区后再提交。",
30964
30967
  "auth.browserLogin": "请在打开的浏览器中完成登录(若未自动打开,请手动访问):",
30965
30968
  "auth.loginSucceeded": "登录成功。",
30966
30969
  "auth.sessionExpired": "登录会话已过期或已被拒绝,请重新发起登录",
@@ -31067,6 +31070,7 @@ var messages2 = {
31067
31070
  "update.remindRun": "请提醒用户在终端运行:",
31068
31071
  "update.restartIde": "更新后请重启 IDE。",
31069
31072
  "error.network": "网络连接失败,请重试",
31073
+ "error.network.timeoutPrefix": "请求超时,时限(毫秒):",
31070
31074
  "error.http.contextSeparator": ",",
31071
31075
  "error.http.notFoundPrefix": "服务端接口不存在(",
31072
31076
  "error.http.notFoundSuffix": ")。请确认当前环境已部署提供此接口的服务端,并检查 API 地址及网关路由。",
@@ -31541,39 +31545,43 @@ var messages3 = {
31541
31545
  ].join("\n"),
31542
31546
  "login.succeeded": "Login successful.",
31543
31547
  "login.saved": "The credential was saved to ~/.evomeme/auth.json.",
31544
- "image.title": "Generate Game Image",
31548
+ "image.title": "Generate Image",
31545
31549
  "image.description": [
31546
- "Generate a game image from a prompt. The tool submits an asynchronous job and polls until it succeeds or fails.",
31547
- "",
31548
- "On success, it downloads the final TOS image to the workspace assets directory and returns the job ID, TOS URL, local path, and an MCP multimodal image block.",
31549
- "Use type asset for transparent game elements and type background for background images.",
31550
- 'For background="transparent", use image_model="asset-max" for text-to-image. asset-pro requires exactly one reference image that already has transparency; asset-lite does not support it.'
31550
+ "Generate an image using the same client capability as the canvas, with model, background, preset size and prompt.",
31551
+ "Poll until success or failure; save the result under workspace assets and return only text metadata such as asset_id, local path and image URL, without image Base64.",
31552
+ 'Defaults: model="asset-pro", background="opaque", size="2048x2048". Transparent text-to-image and auto background require asset-max. asset-lite ignores opaque background settings.',
31553
+ "This tool generates an image. Invoke smart cutout separately for background removal, or use color removal if smart cutout is unavailable."
31551
31554
  ].join("\n"),
31552
31555
  "image.prompt": "Image generation prompt. Required.",
31553
- "image.type": "Image type. Defaults to background.",
31554
- "image.size": "WIDTHxHEIGHT or auto; default 2048x2048. Pro pixels: 921600..4624220; Lite: 3686400..16777216; both aspect ratios 1:16..16:1. Max pixels: 655360..8294400, edges <= 3840 and multiples of 16, aspect ratio 1:3..3:1. Pro/Lite auto uses 2048x2048.",
31555
- "image.references": "Optional reference image URL or array. At most 10 for asset-pro, 14 for asset-lite, or 16 for asset-max.",
31556
- "image.model": "Image model. Defaults to asset-lite; asset-max and asset-pro are also supported.",
31557
- "image.background": "Optional background mode. auto uses the provider default on Pro/Lite. Lite rejects transparent. Pro transparent requires one reference image with transparency. Max supports all three modes.",
31558
- "image.workspace": "Optional absolute Agent workspace path. Defaults to the MCP process working directory.",
31556
+ "image.size": "Preset size, default 2048x2048. Must be supported by the selected model. asset-lite does not support 1024x1024; asset-max dimensions must satisfy its 16-pixel multiple constraint.",
31557
+ "image.model": "Model, default asset-pro. Options: asset-max, asset-pro, asset-lite.",
31558
+ "image.background": "Background, default opaque. auto and transparent without references require asset-max. asset-lite does not offer background switching.",
31559
+ "image.workspace": "Optional absolute workspace path. Defaults to the MCP process working directory. Source asset IDs are relative to this workspace.",
31559
31560
  "image.missingPrompt": "Missing required parameter: prompt",
31560
- "image.invalidType": "Invalid image_type: ",
31561
- "image.invalidSize": "Invalid size: ",
31562
- "image.invalidModel": "Invalid image_model: ",
31563
- "image.invalidBackground": "Invalid background: ",
31564
- "image.emptyReferences": "reference_image_url cannot be empty",
31565
- "image.tooManyReferences": "reference_image_url supports at most 14 reference images",
31561
+ "image.invalidSize": "Preset size is not supported by the selected model: ",
31562
+ "image.autoBackgroundMax": "auto background requires asset-max.",
31566
31563
  "image.completed": "Image generation completed",
31567
- "cutout.title": "Cut Out Game Image",
31564
+ "imageTool.assetId": "Workspace image asset ID, such as assets/hero.png. Use the asset_id returned by an image tool.",
31565
+ "imageTool.unknownParameter": "Unsupported parameter: ",
31566
+ "imageTool.invalidParameter": "Invalid parameter: ",
31567
+ "imageTool.missingParameter": "Missing required parameter: ",
31568
+ "imageTool.sourceRequired": "Provide exactly one of id or image_url.",
31569
+ "imageTool.invalidUrl": "image_url must be a valid HTTP/HTTPS image URL.",
31570
+ "cutout.title": "Smart Cutout",
31568
31571
  "cutout.description": [
31569
- "Remove the background from an existing image. The tool submits an asynchronous job and polls until it succeeds or fails.",
31570
- "",
31571
- "On success, it downloads the final TOS image to the workspace assets directory and returns the job ID, TOS URL, local path, and an MCP multimodal image block."
31572
+ "Use the same smart-cutout client capability as the canvas. Provide exactly one workspace image id or image_url.",
31573
+ "Poll the mask job, compose a transparent PNG locally and upload it. Return only text metadata such as asset_id, local path and image URL, without image Base64.",
31574
+ "Errors are passed through to the client. If the service is unavailable, invoke evomeme_color_key on the original asset."
31572
31575
  ].join("\n"),
31573
- "cutout.imageUrl": "Source image URL. Required.",
31574
- "cutout.workspace": "Optional absolute Agent workspace path. Defaults to the MCP process working directory.",
31575
- "cutout.missingImageUrl": "Missing required parameter: image_url",
31576
- "cutout.completed": "Image cutout completed",
31576
+ "cutout.imageUrl": "HTTP/HTTPS source image URL; provide this or id.",
31577
+ "cutout.completed": "Smart cutout completed",
31578
+ "colorKey.title": "Remove Color",
31579
+ "colorKey.description": "Use the same local src/image algorithm as canvas color removal to produce a transparent PNG. Does not require the smart-cutout service or authentication and can be used when smart cutout is unavailable. Input a workspace source image id; return only text metadata such as asset_id and local path, without image Base64.",
31580
+ "colorKey.color": "Color to remove, #RRGGBB, default #ffffff.",
31581
+ "colorKey.strength": "Removal strength from 0 to 100, default 80.",
31582
+ "colorKey.contiguousOnly": "Only remove matching regions connected to an image boundary, default false.",
31583
+ "colorKey.despill": "Remove background-color spill from retained edge pixels, default false.",
31584
+ "colorKey.completed": "Color removal completed",
31577
31585
  "video.title": "Generate Game Video",
31578
31586
  "video.description": [
31579
31587
  "Generate a game video from a prompt and poll the submitted job until it succeeds or fails.",
@@ -31611,17 +31619,22 @@ var messages3 = {
31611
31619
  "",
31612
31620
  "Before calling, briefly review the main source files for unsafe behavior such as download-and-execute code, reverse shells, miners, long encoded eval payloads, or suspicious install scripts.",
31613
31621
  "",
31614
- "Flow: sign in → read or create the project binding → collect files using .evomemeignore → submit → store checkpoint_id.",
31622
+ "Flow: sign in → read the existing project binding → collect files using .evomemeignore → submit once → store checkpoint_id on success.",
31623
+ "Missing bindings and submission failures return the actual error without creating a thread, re-authenticating to resubmit, or retrying. For a new thread, explicitly call evomeme_reinit_project and then submit separately.",
31615
31624
  "Binary assets cannot be submitted as source. Upload them with evomeme_upload_game_asset, add their paths to .evomemeignore, write asset_url to /public/config.json, rebuild, and submit again.",
31616
31625
  "",
31617
31626
  "Optional parameters: name changes the project display name; cover_url updates the game cover already uploaded to TOS."
31618
31627
  ].join("\n"),
31619
31628
  "submit.workspace": "Absolute workspace root. Defaults to the MCP process working directory, usually the project opened by the IDE.",
31620
- "submit.name": "Optional project display name. Updates the cloud name and is also used when creating a missing binding.",
31629
+ "submit.name": "Optional project display name. Updates the cloud name.",
31621
31630
  "submit.cover": "Optional game cover URL. It must be a public image URL already uploaded to TOS.",
31622
31631
  "submit.succeeded": "Code submission succeeded.",
31623
31632
  "submit.workspaceLabel": "Workspace: ",
31624
31633
  "submit.fileCountLabel": "File count: ",
31634
+ "reinit.title": "Reinitialize Project Binding",
31635
+ "reinit.description": "Create a new cloud thread for the workspace, then replace thread_id in .evomeme/config.json and clear the old checkpoint_id only after creation succeeds. Always creates a new ID, even with an existing binding; also supports first initialization. Only creates and binds: no file submission, deletion of the old cloud project, or automatic retry. Failures return the actual error and preserve the old binding. Call only when the user explicitly requests initialization or rebinding, never as automatic recovery after submission failure.",
31636
+ "reinit.name": "Optional display name for the new cloud project.",
31637
+ "reinit.succeeded": "Workspace project binding initialized successfully.",
31625
31638
  "upload.title": "Upload Game Asset",
31626
31639
  "upload.description": [
31627
31640
  "Send a local binary file to the evomeme server for upload to TOS and return a public asset_url.",
@@ -31670,39 +31683,43 @@ var messages4 = {
31670
31683
  ].join("\n"),
31671
31684
  "login.succeeded": "登录成功。",
31672
31685
  "login.saved": "凭证已写入 ~/.evomeme/auth.json。",
31673
- "image.title": "生成游戏图片",
31686
+ "image.title": "图片生成",
31674
31687
  "image.description": [
31675
- "根据提示词生成游戏图片。工具会提交异步任务并持续轮询,直到任务成功或失败。",
31676
- "",
31677
- "成功后会下载最终 TOS 图片到工作区 assets 目录,并返回 job_id、TOS URL、本地路径以及 MCP 多模态 image 内容块(Base64)。",
31678
- "asset 类型用于生成透明底游戏元素(has_transparent_background=true),background 类型用于生成背景图。",
31679
- 'background="transparent":文生透明图使用 image_model="asset-max";asset-pro 必须提供恰好一张本身带透明通道的参考图;asset-lite 不支持。'
31688
+ "与画布图片生成功能共用客户端能力,按模型、背景、预设尺寸和提示词生成图片。",
31689
+ "工具持续轮询到成功或失败;成功后保存到工作区 assets,仅返回 asset_id、本地路径、图片 URL 等文本元数据,不返回图片 Base64。",
31690
+ '默认 model="asset-pro"、background="opaque"、size="2048x2048"。透明文生图使用 asset-max;auto 背景仅 asset-max 可选,asset-lite 忽略不透明背景设置。',
31691
+ "本工具仅生成图片。需要去除背景时,另行调用智能抠图;智能抠图不可用时可使用颜色剔除。"
31680
31692
  ].join("\n"),
31681
31693
  "image.prompt": "生图提示词,必填。",
31682
- "image.type": "图片类型,默认 background。",
31683
- "image.size": "尺寸 WIDTHxHEIGHT 或 auto,默认 2048x2048。Pro 总像素 921600..4624220;Lite 3686400..16777216,二者宽高比 1:16..16:1。Max 总像素 655360..8294400,边长不超过 3840 且为 16 的倍数,宽高比 1:3..3:1。auto 在 Pro/Lite 使用 2048x2048。",
31684
- "image.references": "可选,一张参考图 URL 或数组。asset-pro 最多 10 张,asset-lite 最多 14 张,asset-max 最多 16 张。",
31685
- "image.model": "生图模型,默认 asset-lite;可选 asset-max、asset-pro。",
31686
- "image.background": "可选背景模式。auto 在 Pro/Lite 中使用上游默认背景;Lite 不支持 transparent;Pro 的 transparent 必须提供一张带透明通道的参考图;Max 支持全部三种模式。",
31687
- "image.workspace": "可选,Agent 工作区绝对路径;默认使用 MCP 进程当前工作目录。",
31694
+ "image.size": "预设尺寸,默认 2048x2048;必须使用所选模型支持的预设。asset-lite 不支持 1024x1024;asset-max 的尺寸须满足其 16 像素倍数限制。",
31695
+ "image.model": "模型,默认 asset-pro;可选 asset-max、asset-pro、asset-lite。",
31696
+ "image.background": "背景,默认 opaque。auto 仅 asset-max 支持;无参考图的 transparent 仅 asset-max 支持;asset-lite 不提供背景切换。",
31697
+ "image.workspace": "可选,工作区绝对路径;默认 MCP 进程当前目录。源素材 ID 相对于该工作区。",
31688
31698
  "image.missingPrompt": "缺少必填参数 prompt",
31689
- "image.invalidType": "无效的 image_type:",
31690
- "image.invalidSize": "无效的 size:",
31691
- "image.invalidModel": "无效的 image_model:",
31692
- "image.invalidBackground": "无效的 background:",
31693
- "image.emptyReferences": "reference_image_url 不能为空",
31694
- "image.tooManyReferences": "reference_image_url 最多支持 14 张参考图",
31695
- "image.completed": "生图任务已完成",
31696
- "cutout.title": "游戏图片抠图",
31699
+ "image.invalidSize": "所选模型不支持此预设尺寸:",
31700
+ "image.autoBackgroundMax": "auto 背景仅适用于 asset-max。",
31701
+ "image.completed": "图片生成已完成",
31702
+ "imageTool.assetId": "工作区图片素材 ID,例如 assets/hero.png;可直接使用图片工具返回的 asset_id。",
31703
+ "imageTool.unknownParameter": "不支持的参数:",
31704
+ "imageTool.invalidParameter": "无效的参数:",
31705
+ "imageTool.missingParameter": "缺少必填参数 ",
31706
+ "imageTool.sourceRequired": "必须且只能提供 id 或 image_url 中的一个。",
31707
+ "imageTool.invalidUrl": "image_url 必须是有效的 HTTP/HTTPS 图片 URL。",
31708
+ "cutout.title": "智能抠图",
31697
31709
  "cutout.description": [
31698
- "对已有图片做透明背景抠图。工具会提交异步任务并持续轮询,直到任务成功或失败。",
31699
- "",
31700
- "成功后会下载最终 TOS 图片到工作区 assets 目录,并返回 job_id、TOS URL、本地路径以及 MCP 多模态 image 内容块(Base64)。"
31710
+ "与画布智能抠图共用客户端能力。通过 id 指定工作区图片,或提供 image_url,两者必须且只能提供一个。",
31711
+ "持续轮询蒙版任务,然后在本地合成透明背景 PNG 并上传,仅返回 asset_id、本地路径、图片 URL 等文本元数据,不返回图片 Base64。",
31712
+ "错误会透传给客户端;服务不可用时,可另行使用 evomeme_color_key 对原始素材进行颜色剔除。"
31701
31713
  ].join("\n"),
31702
- "cutout.imageUrl": "待抠图的原图 URL,必填。",
31703
- "cutout.workspace": "可选,Agent 工作区绝对路径;默认使用 MCP 进程当前工作目录。",
31704
- "cutout.missingImageUrl": "缺少必填参数 image_url",
31705
- "cutout.completed": "抠图任务已完成",
31714
+ "cutout.imageUrl": "待抠图的 HTTP/HTTPS 原图 URL;与 id 二选一。",
31715
+ "cutout.completed": "智能抠图已完成",
31716
+ "colorKey.title": "颜色剔除",
31717
+ "colorKey.description": "与画布颜色剔除共用 src/image 的本地算法。按指定颜色生成透明 PNG;不依赖智能抠图服务或登录,可在智能抠图不可用时使用。输入工作区原图 id,仅返回 asset_id、本地路径等文本元数据,不返回图片 Base64。",
31718
+ "colorKey.color": "剔除颜色,格式 #RRGGBB,默认 #ffffff。",
31719
+ "colorKey.strength": "剔除强度,0–100,默认 80。",
31720
+ "colorKey.contiguousOnly": "是否仅剔除与图像边界相连的连续区域,默认 false。",
31721
+ "colorKey.despill": "是否滤色,消除保留边缘像素中的背景色溢出,默认 false。",
31722
+ "colorKey.completed": "颜色剔除已完成",
31706
31723
  "video.title": "生成游戏视频",
31707
31724
  "video.description": [
31708
31725
  "根据提示词生成游戏视频,提交任务后持续轮询直到成功或失败。",
@@ -31740,17 +31757,22 @@ var messages4 = {
31740
31757
  "",
31741
31758
  "调用前请快速阅读将提交的主要源码,做一次浅层安全复检。若发现下载执行、反弹壳、挖矿、长编码 eval、可疑 install 脚本等危险代码,不要调用本工具,并告知用户。",
31742
31759
  "",
31743
- "流程:检查/完成登录 → 读取或创建作品绑定 → 按 .evomemeignore 收集文件(必须包含 dist/)→ 提交 → 写入 checkpoint_id。",
31760
+ "流程:检查/完成登录 → 读取已有作品绑定 → 按 .evomemeignore 收集文件(必须包含 dist/)→ 提交一次 → 成功后写入 checkpoint_id。",
31761
+ "缺少绑定或提交失败时直接返回真实错误,不自动创建 thread、重新登录后提交或重试。需要新 thread 时,显式调用 evomeme_reinit_project,成功后再另行提交。",
31744
31762
  "二进制资源不能作为源码直接提交;请先用 evomeme_upload_game_asset 上传,把本地路径加入 .evomemeignore,并将 asset_url 写入 /public/config.json,再重新构建并提交。",
31745
31763
  "",
31746
31764
  "可选参数:name 用于修改作品显示名称;cover_url 用于更新已上传到 TOS 的游戏封面。"
31747
31765
  ].join("\n"),
31748
31766
  "submit.workspace": "工作区根目录绝对路径。省略时使用 MCP 进程当前工作目录(通常即 IDE 打开的项目根)。",
31749
- "submit.name": "作品显示名称(可选)。传入时更新云端作品名;本地尚无绑定时也用于新建作品。",
31767
+ "submit.name": "作品显示名称(可选)。传入时更新云端作品名。",
31750
31768
  "submit.cover": "游戏封面图 URL(可选)。必须是已经上传到 TOS 的公开图片地址。",
31751
31769
  "submit.succeeded": "代码提交成功。",
31752
31770
  "submit.workspaceLabel": "工作区:",
31753
31771
  "submit.fileCountLabel": "文件数:",
31772
+ "reinit.title": "重新初始化作品绑定",
31773
+ "reinit.description": "为当前工作区创建一个新的云端 thread,并在创建成功后替换 .evomeme/config.json 中的 thread_id、清除旧 checkpoint_id。即使已有绑定也创建新 id;没有绑定时可用于首次初始化。只负责创建与绑定,不提交文件、不删除旧云端作品、不自动重试。失败直接返回真实错误,保留旧绑定。只在用户明确需要初始化或重新绑定时调用,不能作为提交失败后的自动恢复步骤。",
31774
+ "reinit.name": "新建云端作品的显示名称(可选)。",
31775
+ "reinit.succeeded": "工作区作品绑定初始化成功。",
31754
31776
  "upload.title": "上传游戏素材",
31755
31777
  "upload.description": [
31756
31778
  "将本地二进制文件交给 evomeme服务端上传到对象存储(TOS),返回公开可访问的 asset_url。",
@@ -32072,10 +32094,10 @@ function assignProp(target, prop, value) {
32072
32094
  configurable: true
32073
32095
  });
32074
32096
  }
32075
- function getElementAtPath(obj, path59) {
32076
- if (!path59)
32097
+ function getElementAtPath(obj, path60) {
32098
+ if (!path60)
32077
32099
  return obj;
32078
- return path59.reduce((acc, key) => acc?.[key], obj);
32100
+ return path60.reduce((acc, key) => acc?.[key], obj);
32079
32101
  }
32080
32102
  function promiseAllObject(promisesObj) {
32081
32103
  const keys = Object.keys(promisesObj);
@@ -32395,11 +32417,11 @@ function aborted(x, startIndex = 0) {
32395
32417
  }
32396
32418
  return false;
32397
32419
  }
32398
- function prefixIssues(path59, issues) {
32420
+ function prefixIssues(path60, issues) {
32399
32421
  return issues.map((iss) => {
32400
32422
  var _a;
32401
32423
  (_a = iss).path ?? (_a.path = []);
32402
- iss.path.unshift(path59);
32424
+ iss.path.unshift(path60);
32403
32425
  return iss;
32404
32426
  });
32405
32427
  }
@@ -39153,7 +39175,7 @@ var StdioClientTransport = class {
39153
39175
  };
39154
39176
 
39155
39177
  // src/version.ts
39156
- var VERSION = "0.1.10-beta.1";
39178
+ var VERSION = "0.1.10-beta.3";
39157
39179
 
39158
39180
  // src/publishChannel.generated.ts
39159
39181
  var PUBLISH_NPM_NAME = "evomeme";
@@ -39581,6 +39603,7 @@ import fs7 from "node:fs";
39581
39603
  import path7 from "node:path";
39582
39604
  import os5 from "node:os";
39583
39605
  var MCP_SERVER_NAME4 = "evomeme";
39606
+ var MCP_TOOL_TIMEOUT_MS = 30 * 60 * 1e3;
39584
39607
  function getWorkbuddyConfigPath(homeDir) {
39585
39608
  return path7.join(homeDir ?? os5.homedir(), ".workbuddy", "mcp.json");
39586
39609
  }
@@ -39595,7 +39618,8 @@ function mergeWorkbuddyMcpConfig(configPath, options) {
39595
39618
  const serverEntry = {
39596
39619
  type: "stdio",
39597
39620
  command: options.launcher.command,
39598
- args: [...options.launcher.args]
39621
+ args: [...options.launcher.args],
39622
+ timeout: MCP_TOOL_TIMEOUT_MS
39599
39623
  };
39600
39624
  const envValues = options.env ?? {};
39601
39625
  if (Object.keys(envValues).length > 0) {
@@ -39642,6 +39666,7 @@ import fs8 from "node:fs";
39642
39666
  import path8 from "node:path";
39643
39667
  import os6 from "node:os";
39644
39668
  var MCP_SERVER_NAME5 = "evomeme";
39669
+ var MCP_TOOL_TIMEOUT_MS2 = 30 * 60 * 1e3;
39645
39670
  var TRAE_APP_DIR_NAME = "TRAE SOLO CN";
39646
39671
  function getTraeAppDataDir(options = {}) {
39647
39672
  if (options.appDataDir) {
@@ -39679,12 +39704,9 @@ function mergeTraeMcpConfig(configPath, options) {
39679
39704
  const mcpServers = root.mcpServers && typeof root.mcpServers === "object" && !Array.isArray(root.mcpServers) ? { ...root.mcpServers } : {};
39680
39705
  const serverEntry = {
39681
39706
  command: options.launcher.command,
39682
- args: [...options.launcher.args]
39707
+ args: [...options.launcher.args],
39708
+ env: { ...options.env, RUN_MCP_TIMEOUT_MS: String(MCP_TOOL_TIMEOUT_MS2) }
39683
39709
  };
39684
- const envValues = options.env ?? {};
39685
- if (Object.keys(envValues).length > 0) {
39686
- serverEntry.env = { ...envValues };
39687
- }
39688
39710
  mcpServers[MCP_SERVER_NAME5] = serverEntry;
39689
39711
  const nextRoot = {
39690
39712
  ...root,
@@ -39767,6 +39789,41 @@ var NetworkError = class extends Error {
39767
39789
  this.name = "NetworkError";
39768
39790
  }
39769
39791
  };
39792
+ function networkRequestError(method, url, cause, timeoutMs, timedOut) {
39793
+ const requestUrl = new URL(url);
39794
+ const details = /* @__PURE__ */ new Set();
39795
+ const seen = /* @__PURE__ */ new Set();
39796
+ function collect(value) {
39797
+ if (value == null || seen.has(value) || seen.size >= 12) return;
39798
+ seen.add(value);
39799
+ if (typeof value === "string") {
39800
+ details.add(value);
39801
+ return;
39802
+ }
39803
+ if (typeof value !== "object") return;
39804
+ const error2 = value;
39805
+ const code = typeof error2.code === "string" ? error2.code : "";
39806
+ const message = typeof error2.message === "string" ? error2.message : "";
39807
+ if (code || message) details.add([code, message].filter(Boolean).join(": "));
39808
+ collect(error2.cause);
39809
+ if (Array.isArray(error2.errors)) error2.errors.forEach(collect);
39810
+ }
39811
+ collect(cause);
39812
+ const safeDetails = [...details].map((detail) => detail.replace(/https?:\/\/[^\s"'<>]+/gi, (raw) => {
39813
+ try {
39814
+ const parsed = new URL(raw);
39815
+ return `${parsed.origin}${parsed.pathname}`;
39816
+ } catch {
39817
+ return "[URL]";
39818
+ }
39819
+ }));
39820
+ return new NetworkError([
39821
+ t("error.network"),
39822
+ `${method} ${requestUrl.origin}${requestUrl.pathname}`,
39823
+ ...timedOut ? [t("error.network.timeoutPrefix") + timeoutMs] : [],
39824
+ ...safeDetails
39825
+ ].join("\n"), cause);
39826
+ }
39770
39827
  var HttpResponseError = class extends Error {
39771
39828
  constructor(status, method, url) {
39772
39829
  const parsed = new URL(url);
@@ -39834,14 +39891,15 @@ async function performRequest(config2, fetchFn, request) {
39834
39891
  const controller = new AbortController();
39835
39892
  const timer = setTimeout(() => controller.abort(), timeoutMs);
39836
39893
  let response;
39894
+ let rawBody;
39837
39895
  try {
39838
39896
  response = await fetchFn(url, { method: request.method, headers, body, signal: controller.signal });
39897
+ rawBody = await response.text();
39839
39898
  } catch (error2) {
39840
- throw new NetworkError(t("error.network"), error2);
39899
+ throw networkRequestError(request.method, url, error2, timeoutMs, controller.signal.aborted);
39841
39900
  } finally {
39842
39901
  clearTimeout(timer);
39843
39902
  }
39844
- const rawBody = await response.text();
39845
39903
  let envelope;
39846
39904
  try {
39847
39905
  envelope = parseEnvelope(rawBody);
@@ -39859,8 +39917,8 @@ async function performRequest(config2, fetchFn, request) {
39859
39917
  }
39860
39918
  return envelope.data;
39861
39919
  }
39862
- function buildUrl(baseUrl, path59, query) {
39863
- const url = new URL(`${baseUrl}${path59.startsWith("/") ? path59 : `/${path59}`}`);
39920
+ function buildUrl(baseUrl, path60, query) {
39921
+ const url = new URL(`${baseUrl}${path60.startsWith("/") ? path60 : `/${path60}`}`);
39864
39922
  if (query) {
39865
39923
  for (const [key, value] of Object.entries(query)) {
39866
39924
  if (value !== void 0) {
@@ -39998,11 +40056,11 @@ async function uploadTosAsset(token, bytes, input = {}, options = {}) {
39998
40056
  async function uploadTosModelAsset(token, bytes, input = {}, options = {}) {
39999
40057
  return uploadApprovedTosAsset("/v1/tos/upload/model", 20 * 6e4, token, bytes, input, options);
40000
40058
  }
40001
- async function uploadApprovedTosAsset(path59, timeoutMs, token, bytes, input, options) {
40059
+ async function uploadApprovedTosAsset(path60, timeoutMs, token, bytes, input, options) {
40002
40060
  const client = createPluginServerClient(options);
40003
40061
  const data = await client.request({
40004
40062
  method: "PUT",
40005
- path: path59,
40063
+ path: path60,
40006
40064
  token,
40007
40065
  query: {
40008
40066
  folder: input.folder,
@@ -40081,7 +40139,6 @@ function isMediaUploadStatus(value) {
40081
40139
  }
40082
40140
 
40083
40141
  // src/api/imageApi.ts
40084
- var GAME_IMAGE_TYPES = ["asset", "background"];
40085
40142
  var GAME_IMAGE_SIZES = [
40086
40143
  "1024x1024",
40087
40144
  "2048x2048",
@@ -40796,6 +40853,12 @@ import fs12 from "node:fs";
40796
40853
  import path12 from "node:path";
40797
40854
 
40798
40855
  // src/project/errors.ts
40856
+ var MissingProjectBindingError = class extends Error {
40857
+ constructor() {
40858
+ super(t("error.project.missingBinding"));
40859
+ this.name = "MissingProjectBindingError";
40860
+ }
40861
+ };
40799
40862
  var BinaryFileError = class extends Error {
40800
40863
  constructor(filePath) {
40801
40864
  super(
@@ -40907,9 +40970,9 @@ async function createAndBindProject(workspaceDir, token, options = {}) {
40907
40970
 
40908
40971
  // src/project/submit.ts
40909
40972
  async function submitWorkspaceCode(workspaceDir, token, options = {}) {
40910
- let config2 = loadProjectConfig(workspaceDir);
40973
+ const config2 = loadProjectConfig(workspaceDir);
40911
40974
  if (!config2) {
40912
- config2 = await createAndBindProject(workspaceDir, token, options);
40975
+ throw new MissingProjectBindingError();
40913
40976
  }
40914
40977
  const { files } = collectWorkspaceFiles(workspaceDir, options);
40915
40978
  const clientOptions = {
@@ -40920,29 +40983,14 @@ async function submitWorkspaceCode(workspaceDir, token, options = {}) {
40920
40983
  name: options.name?.trim() || void 0,
40921
40984
  coverUrl: options.coverUrl?.trim() || void 0
40922
40985
  };
40923
- try {
40924
- return await submitAndRecordCheckpoint(
40925
- workspaceDir,
40926
- token,
40927
- config2.threadId,
40928
- files,
40929
- clientOptions,
40930
- meta
40931
- );
40932
- } catch (error2) {
40933
- if (!(error2 instanceof ThreadInvalidError)) {
40934
- throw error2;
40935
- }
40936
- config2 = await createAndBindProject(workspaceDir, token, options);
40937
- return await submitAndRecordCheckpoint(
40938
- workspaceDir,
40939
- token,
40940
- config2.threadId,
40941
- files,
40942
- clientOptions,
40943
- meta
40944
- );
40945
- }
40986
+ return await submitAndRecordCheckpoint(
40987
+ workspaceDir,
40988
+ token,
40989
+ config2.threadId,
40990
+ files,
40991
+ clientOptions,
40992
+ meta
40993
+ );
40946
40994
  }
40947
40995
  async function submitAndRecordCheckpoint(workspaceDir, token, threadId, files, clientOptions, meta) {
40948
40996
  const result = await submitProject(
@@ -45565,10 +45613,6 @@ async function executeImageGeneration(workspaceDir, body, imageServiceDeps) {
45565
45613
  if (!completed?.outputAssetId || !completed.outputLocalPath) {
45566
45614
  throw new Error(t("error.task.imageOutputMissing"));
45567
45615
  }
45568
- if (body.image_type === "asset") {
45569
- const result = await executeImageCutout(workspaceDir, { image_url: generated.assetUrl, source_id: completed.outputAssetId }, imageServiceDeps);
45570
- return { ...result, source_job_id: generated.jobId, image_type: "asset", has_transparent_background: true, size: generated.size };
45571
- }
45572
45616
  return {
45573
45617
  generation: generated,
45574
45618
  local_path: artifact.localPath,
@@ -48042,6 +48086,64 @@ async function createColorKeyPreview(workspaceDir, body) {
48042
48086
  throw error2;
48043
48087
  }
48044
48088
  }
48089
+ async function executeColorKey(workspaceDir, body) {
48090
+ if (body.preview_token !== void 0) return applyColorKeyPreview(workspaceDir, body);
48091
+ const input = normalizeColorKeyInput(body);
48092
+ const sourcePath = resolveAssetPath(workspaceDir, input.sourceAssetId);
48093
+ if (mediaKind(path35.extname(sourcePath)) !== "image") {
48094
+ throw httpError(400, t("error.task.colorSourceImage"));
48095
+ }
48096
+ const paths = await ensureWorkspace(workspaceDir);
48097
+ const operationId = String(body.client_operation_id || createOperationId());
48098
+ const outputPath = path35.join(paths.assetsDir, `${operationId}_color-key.png`);
48099
+ const job = await beginTask({
48100
+ operationId,
48101
+ workspaceDir,
48102
+ kind: "color_key",
48103
+ parentAssetIds: [input.sourceAssetId],
48104
+ outputKind: "image",
48105
+ metadata: {
48106
+ color: input.color,
48107
+ strength: input.strength,
48108
+ contiguousOnly: input.contiguousOnly,
48109
+ despill: input.despill,
48110
+ perceptualColor: "oklab",
48111
+ featheredEdge: true
48112
+ }
48113
+ });
48114
+ try {
48115
+ await updateTask(workspaceDir, job.operationId, { status: "RUNNING" });
48116
+ const result = await processColorKey(sourcePath, outputPath, input);
48117
+ const completed = await completeTask({
48118
+ operationId: job.operationId,
48119
+ workspaceDir,
48120
+ localPath: result.file,
48121
+ mimeType: "image/png",
48122
+ metadata: { ...job.metadata, width: result.width, height: result.height }
48123
+ });
48124
+ if (!completed?.outputAssetId || !completed.outputLocalPath) throw new Error(t("error.task.colorOutputMissing"));
48125
+ return {
48126
+ id: completed.outputAssetId,
48127
+ name: path35.basename(completed.outputLocalPath),
48128
+ url: `/files/${completed.outputAssetId}`,
48129
+ local_path: completed.outputLocalPath,
48130
+ mime_type: "image/png",
48131
+ source_id: input.sourceAssetId,
48132
+ operation_id: job.operationId,
48133
+ width: result.width,
48134
+ height: result.height,
48135
+ color: result.color,
48136
+ strength: result.strength,
48137
+ contiguous_only: result.contiguousOnly,
48138
+ despill: result.despill,
48139
+ has_transparent_background: true
48140
+ };
48141
+ } catch (error2) {
48142
+ await unlink4(outputPath).catch(() => void 0);
48143
+ await failTask(workspaceDir, job.operationId, error2).catch(() => void 0);
48144
+ throw error2;
48145
+ }
48146
+ }
48045
48147
  async function applyColorKeyPreview(workspaceDir, body) {
48046
48148
  const input = normalizeColorKeyInput(body);
48047
48149
  const token = normalizeColorKeyPreviewToken(body.preview_token);
@@ -48937,13 +49039,13 @@ function errorMessage(error2) {
48937
49039
  }
48938
49040
  function canvasGenerationErrorMessage(error2) {
48939
49041
  const message = incompatibleResponseDetail(error2) ?? errorMessage(error2);
49042
+ if (error2 instanceof Error && error2.name === "NetworkError") return message;
48940
49043
  const normalized = message.toLowerCase();
48941
49044
  if (/insufficient[_ -]?quota|quota (?:has been )?(?:exceeded|exhausted)|insufficient (?:balance|credit|funds)|credit balance (?:is )?too low/i.test(message) || /(?:额度(?:不足|已?用完|耗尽)|余额(?:不足|已?用完))/.test(message)) {
48942
49045
  return CANVAS_QUOTA_ERROR;
48943
49046
  }
48944
49047
  const code = String(error2?.code || "").toUpperCase();
48945
- const name = error2 instanceof Error ? error2.name : "";
48946
- if (name === "NetworkError" || ["ECONNABORTED", "ECONNREFUSED", "ECONNRESET", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT"].includes(code) || /fetch failed|network(?: request)? (?:error|failed)|socket hang up|connection (?:error|failed|refused|reset|timed out)|request timed out|connecttimeout|网络异常|连接失败/.test(normalized)) {
49048
+ if (["ECONNABORTED", "ECONNREFUSED", "ECONNRESET", "EHOSTUNREACH", "ENETUNREACH", "ETIMEDOUT"].includes(code) || /fetch failed|network(?: request)? (?:error|failed)|socket hang up|connection (?:error|failed|refused|reset|timed out)|request timed out|connecttimeout|网络异常|连接失败/.test(normalized)) {
48947
49049
  return CANVAS_NETWORK_ERROR;
48948
49050
  }
48949
49051
  return message;
@@ -49099,7 +49201,7 @@ async function executeOperation(name, workspace, input, deps) {
49099
49201
  case "eliminate_scroll_background_seam":
49100
49202
  return executeScrollBackgroundSeamElimination(workspace, input, deps.image);
49101
49203
  case "color_key":
49102
- return applyColorKeyPreview(workspace, input);
49204
+ return executeColorKey(workspace, input);
49103
49205
  case "import_color_key":
49104
49206
  return applyBrowserColorKeyResult(workspace, Buffer.from(String(input.png), "base64"), new URL(String(input.url), "http://localhost"));
49105
49207
  case "import_perfect_pixel":
@@ -53152,14 +53254,6 @@ var Server = class extends Protocol {
53152
53254
  function toolOk(text) {
53153
53255
  return { content: [{ type: "text", text }] };
53154
53256
  }
53155
- function toolOkWithImage(text, data, mimeType3) {
53156
- return {
53157
- content: [
53158
- { type: "text", text },
53159
- { type: "image", data, mimeType: mimeType3 }
53160
- ]
53161
- };
53162
- }
53163
53257
  function toolError(error2) {
53164
53258
  const text = error2 instanceof Error ? error2.message : String(error2);
53165
53259
  return { content: [{ type: "text", text }], isError: true };
@@ -53263,8 +53357,51 @@ async function handleLoginEvomeme(deps = defaultDeps4) {
53263
53357
  }
53264
53358
  }
53265
53359
 
53266
- // src/mcp/tools/submitGameCode.ts
53360
+ // src/mcp/tools/reinitProject.ts
53267
53361
  import path52 from "node:path";
53362
+ var REINIT_PROJECT_TOOL_NAME = "evomeme_reinit_project";
53363
+ var reinitProjectTool = {
53364
+ name: REINIT_PROJECT_TOOL_NAME,
53365
+ description: mcpT("reinit.description"),
53366
+ inputSchema: {
53367
+ type: "object",
53368
+ properties: {
53369
+ workspace_dir: { type: "string", description: mcpT("submit.workspace") },
53370
+ name: { type: "string", description: mcpT("reinit.name") }
53371
+ },
53372
+ additionalProperties: false
53373
+ },
53374
+ annotations: {
53375
+ title: mcpT("reinit.title"),
53376
+ destructiveHint: true,
53377
+ idempotentHint: false,
53378
+ openWorldHint: true,
53379
+ readOnlyHint: false
53380
+ }
53381
+ };
53382
+ var defaultDeps5 = {
53383
+ ensureAuthenticated,
53384
+ createAndBindProject,
53385
+ cwd: () => process.cwd()
53386
+ };
53387
+ async function handleReinitProject(args = {}, deps = defaultDeps5) {
53388
+ try {
53389
+ const workspaceDir = path52.resolve(args.workspace_dir?.trim() || deps.cwd());
53390
+ const name = args.name?.trim();
53391
+ const token = await deps.ensureAuthenticated();
53392
+ const result = await deps.createAndBindProject(workspaceDir, token, name ? { name } : {});
53393
+ return toolOk([
53394
+ mcpT("reinit.succeeded"),
53395
+ mcpT("submit.workspaceLabel") + workspaceDir,
53396
+ `thread_id:${result.threadId}`
53397
+ ].join("\n"));
53398
+ } catch (error2) {
53399
+ return toolError(error2);
53400
+ }
53401
+ }
53402
+
53403
+ // src/mcp/tools/submitGameCode.ts
53404
+ import path53 from "node:path";
53268
53405
  var SUBMIT_GAME_CODE_TOOL_NAME = "evomeme_submit_game_code";
53269
53406
  var SUBMIT_GAME_CODE_DESCRIPTION = mcpT("submit.description");
53270
53407
  var submitGameCodeTool = {
@@ -53296,14 +53433,14 @@ var submitGameCodeTool = {
53296
53433
  readOnlyHint: false
53297
53434
  }
53298
53435
  };
53299
- var defaultDeps5 = {
53436
+ var defaultDeps6 = {
53300
53437
  ensureAuthenticated,
53301
53438
  submitWorkspaceCode,
53302
53439
  cwd: () => process.cwd()
53303
53440
  };
53304
- async function handleSubmitGameCode(args = {}, deps = defaultDeps5) {
53441
+ async function handleSubmitGameCode(args = {}, deps = defaultDeps6) {
53305
53442
  try {
53306
- const workspaceDir = path52.resolve(args.workspace_dir?.trim() || deps.cwd());
53443
+ const workspaceDir = path53.resolve(args.workspace_dir?.trim() || deps.cwd());
53307
53444
  const submitOptions = {};
53308
53445
  const name = args.name?.trim();
53309
53446
  if (name) {
@@ -53313,11 +53450,8 @@ async function handleSubmitGameCode(args = {}, deps = defaultDeps5) {
53313
53450
  if (coverUrl) {
53314
53451
  submitOptions.coverUrl = coverUrl;
53315
53452
  }
53316
- const authOptions = {};
53317
- const result = await withAuthRetry(
53318
- (token) => deps.submitWorkspaceCode(workspaceDir, token, submitOptions),
53319
- { ...authOptions, ensureAuthenticatedFn: deps.ensureAuthenticated }
53320
- );
53453
+ const token = await deps.ensureAuthenticated();
53454
+ const result = await deps.submitWorkspaceCode(workspaceDir, token, submitOptions);
53321
53455
  return toolOk(formatSuccess(workspaceDir, result));
53322
53456
  } catch (error2) {
53323
53457
  return toolError(error2);
@@ -53334,7 +53468,7 @@ function formatSuccess(workspaceDir, result) {
53334
53468
  }
53335
53469
 
53336
53470
  // src/mcp/tools/uploadGameAsset.ts
53337
- import path53 from "node:path";
53471
+ import path54 from "node:path";
53338
53472
  var UPLOAD_GAME_ASSET_TOOL_NAME = "evomeme_upload_game_asset";
53339
53473
  var UPLOAD_GAME_ASSET_DESCRIPTION = mcpT("upload.description");
53340
53474
  var uploadGameAssetTool = {
@@ -53367,17 +53501,17 @@ var uploadGameAssetTool = {
53367
53501
  readOnlyHint: false
53368
53502
  }
53369
53503
  };
53370
- var defaultDeps6 = {
53504
+ var defaultDeps7 = {
53371
53505
  ensureAuthenticated,
53372
53506
  uploadLocalAsset,
53373
53507
  cwd: () => process.cwd()
53374
53508
  };
53375
- async function handleUploadGameAsset(args, deps = defaultDeps6) {
53509
+ async function handleUploadGameAsset(args, deps = defaultDeps7) {
53376
53510
  try {
53377
53511
  if (!args.local_path?.trim()) {
53378
53512
  return toolError(new Error(mcpT("upload.missingLocalPath")));
53379
53513
  }
53380
- const localPath = path53.isAbsolute(args.local_path) ? args.local_path : path53.resolve(deps.cwd(), args.local_path);
53514
+ const localPath = path54.isAbsolute(args.local_path) ? args.local_path : path54.resolve(deps.cwd(), args.local_path);
53381
53515
  const uploadOptions = {};
53382
53516
  if (args.folder !== void 0) {
53383
53517
  uploadOptions.folder = args.folder;
@@ -53406,13 +53540,13 @@ function formatSuccess2(result) {
53406
53540
 
53407
53541
  // src/tasks/client.ts
53408
53542
  import { spawn as spawn8 } from "node:child_process";
53409
- import path54 from "node:path";
53543
+ import path55 from "node:path";
53410
53544
  var INTERNAL_TASK_WORKER_COMMAND = "__evomeme_task_worker";
53411
53545
  var runDetachedTask = async (kind, input, workspace) => {
53412
53546
  const task = await createTask(workspace, kind, input, "mcp");
53413
53547
  if (!process.argv[1]) throw new Error(t("error.task.workerEntryMissing"));
53414
- const entry = path54.resolve(process.argv[1]);
53415
- const execArgs = path54.extname(entry) === ".ts" ? process.execArgv.filter((arg) => !arg.startsWith("--inspect") && !arg.startsWith("--debug")) : [];
53548
+ const entry = path55.resolve(process.argv[1]);
53549
+ const execArgs = path55.extname(entry) === ".ts" ? process.execArgv.filter((arg) => !arg.startsWith("--inspect") && !arg.startsWith("--debug")) : [];
53416
53550
  const child = spawn8(process.execPath, [...execArgs, entry, INTERNAL_TASK_WORKER_COMMAND, task.workspaceDir, task.operationId], {
53417
53551
  cwd: task.workspaceDir,
53418
53552
  env: process.env,
@@ -53439,11 +53573,46 @@ async function runTaskWorker(workspace, id) {
53439
53573
  }
53440
53574
  }
53441
53575
 
53576
+ // src/mcp/imageTool.ts
53577
+ var defaultImageToolDeps = { ...defaultImageServiceDeps, taskRunner: runDetachedTask };
53578
+ function validateImageToolInput(tool, args) {
53579
+ const values = args;
53580
+ const properties = tool.inputSchema.properties;
53581
+ for (const [key, value] of Object.entries(values)) {
53582
+ if (value === void 0) continue;
53583
+ const field = properties[key];
53584
+ if (!field) throw new Error(`${mcpT("imageTool.unknownParameter")}${key}`);
53585
+ if (typeof value !== field.type || typeof value === "number" && (!Number.isFinite(value) || field.minimum !== void 0 && value < field.minimum || field.maximum !== void 0 && value > field.maximum) || field.enum && !field.enum.includes(value)) {
53586
+ throw new Error(`${mcpT("imageTool.invalidParameter")}${key}: ${String(value)}`);
53587
+ }
53588
+ }
53589
+ for (const key of tool.inputSchema.required ?? []) {
53590
+ if (values[key] === void 0 || values[key] === "") throw new Error(`${mcpT("imageTool.missingParameter")}${key}`);
53591
+ }
53592
+ }
53593
+ function imageToolSource(args) {
53594
+ const id = args.id?.trim();
53595
+ const url = args.image_url?.trim();
53596
+ if (args.id !== void 0 === (args.image_url !== void 0) || args.id !== void 0 && !id || args.image_url !== void 0 && !url) {
53597
+ throw new Error(mcpT("imageTool.sourceRequired"));
53598
+ }
53599
+ if (id) return { id };
53600
+ let valid = false;
53601
+ try {
53602
+ valid = ["http:", "https:"].includes(new URL(url).protocol);
53603
+ } catch {
53604
+ }
53605
+ if (!valid) throw new Error(mcpT("imageTool.invalidUrl"));
53606
+ return { image_url: url };
53607
+ }
53608
+ function runImageToolTask(name, parameters, workspace, deps) {
53609
+ return deps.taskRunner ? deps.taskRunner(name, parameters, workspace) : runTask(workspace, name, parameters, "mcp", { image: deps });
53610
+ }
53611
+
53442
53612
  // src/mcp/imageJob.ts
53443
- import { readFile as readFile19 } from "node:fs/promises";
53444
53613
  function imageArtifactToolResult(input) {
53445
53614
  const detailLines = Object.entries(input.details ?? {}).map(([key, value]) => `${key}: ${value}`);
53446
- return toolOkWithImage(
53615
+ return toolOk(
53447
53616
  [
53448
53617
  `${input.successLabel}。`,
53449
53618
  `job_id: ${input.jobId}`,
@@ -53453,28 +53622,32 @@ function imageArtifactToolResult(input) {
53453
53622
  `attempts: ${input.attempts}/${input.maxAttempts}`,
53454
53623
  ...detailLines,
53455
53624
  `local_path: ${input.artifact.localPath}`,
53456
- `multimodal_base64: content[1].data (${input.artifact.base64.length} characters)`,
53457
53625
  `mime_type: ${input.artifact.mimeType}`
53458
- ].join("\n"),
53459
- input.artifact.base64,
53460
- input.artifact.mimeType
53626
+ ].join("\n")
53461
53627
  );
53462
53628
  }
53463
53629
  async function formatImageTaskResult(result, successLabel) {
53464
53630
  const generated = result.generation;
53465
- const bytes = await readFile19(String(result.local_path));
53466
53631
  const details = {};
53467
- for (const key of ["size", "image_type", "has_transparent_background", "source_job_id", "mask_url"]) {
53632
+ for (const key of ["model", "size", "has_transparent_background", "mask_url", "source_id", "width", "height", "color", "strength", "contiguous_only", "despill"]) {
53468
53633
  const value = result[key];
53469
53634
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") details[key] = value;
53470
53635
  }
53636
+ if (typeof result.id === "string") details.asset_id = result.id;
53637
+ if (!generated) {
53638
+ return toolOk([
53639
+ successLabel,
53640
+ "status: SUCCEEDED",
53641
+ `operation_id: ${result.operation_id}`,
53642
+ ...Object.entries(details).map(([key, value]) => `${key}: ${value}`),
53643
+ `local_path: ${result.local_path}`,
53644
+ `mime_type: ${result.mime_type}`
53645
+ ].join("\n"));
53646
+ }
53471
53647
  return imageArtifactToolResult({
53472
53648
  artifact: {
53473
- bytes,
53474
- base64: bytes.toString("base64"),
53475
53649
  mimeType: String(result.mime_type),
53476
- localPath: String(result.local_path),
53477
- assetUrl: generated.assetUrl
53650
+ localPath: String(result.local_path)
53478
53651
  },
53479
53652
  jobId: generated.jobId,
53480
53653
  assetUrl: generated.assetUrl,
@@ -53495,106 +53668,35 @@ var generateGameImageTool = {
53495
53668
  inputSchema: {
53496
53669
  type: "object",
53497
53670
  properties: {
53498
- prompt: {
53499
- type: "string",
53500
- description: mcpT("image.prompt")
53501
- },
53502
- image_type: {
53503
- type: "string",
53504
- enum: [...GAME_IMAGE_TYPES],
53505
- description: mcpT("image.type")
53506
- },
53507
- size: {
53508
- type: "string",
53509
- description: mcpT("image.size")
53510
- },
53511
- reference_image_url: {
53512
- oneOf: [
53513
- { type: "string" },
53514
- { type: "array", items: { type: "string" }, minItems: 1, maxItems: 16 }
53515
- ],
53516
- description: mcpT("image.references")
53517
- },
53518
- image_model: {
53519
- type: "string",
53520
- enum: [...GAME_IMAGE_MODELS],
53521
- description: mcpT("image.model")
53522
- },
53523
- background: {
53524
- type: "string",
53525
- enum: [...GAME_IMAGE_BACKGROUNDS],
53526
- description: mcpT("image.background")
53527
- },
53528
- workspace_dir: {
53529
- type: "string",
53530
- description: mcpT("image.workspace")
53531
- }
53671
+ model: { type: "string", enum: [...GAME_IMAGE_MODELS], default: "asset-pro", description: mcpT("image.model") },
53672
+ background: { type: "string", enum: [...GAME_IMAGE_BACKGROUNDS], default: "opaque", description: mcpT("image.background") },
53673
+ size: { type: "string", enum: [...GAME_IMAGE_SIZES], default: "2048x2048", description: mcpT("image.size") },
53674
+ prompt: { type: "string", description: mcpT("image.prompt") },
53675
+ workspace_dir: { type: "string", description: mcpT("image.workspace") }
53532
53676
  },
53533
53677
  required: ["prompt"],
53534
53678
  additionalProperties: false
53535
53679
  },
53536
- annotations: {
53537
- title: mcpT("image.title"),
53538
- destructiveHint: false,
53539
- idempotentHint: false,
53540
- openWorldHint: true,
53541
- readOnlyHint: false
53542
- }
53680
+ annotations: { title: mcpT("image.title"), destructiveHint: false, idempotentHint: false, openWorldHint: true, readOnlyHint: false }
53543
53681
  };
53544
- var defaultDeps7 = { ...defaultImageServiceDeps, taskRunner: runDetachedTask };
53545
- async function handleGenerateGameImage(args, deps = defaultDeps7) {
53682
+ async function handleGenerateGameImage(args, deps = defaultImageToolDeps) {
53546
53683
  try {
53547
- if (!args.prompt?.trim()) throw new Error(mcpT("image.missingPrompt"));
53548
- const input = buildGenerateInput(args);
53549
- if (input instanceof Error) throw input;
53550
- const parameters = {
53551
- prompt: input.prompt,
53552
- model: input.imageModel,
53553
- size: input.size,
53554
- image_type: input.imageType,
53555
- background: input.background,
53556
- reference_image_urls: input.referenceImageUrl === void 0 ? void 0 : Array.isArray(input.referenceImageUrl) ? input.referenceImageUrl : [input.referenceImageUrl]
53557
- };
53558
- const workspace = args.workspace_dir?.trim() || deps.cwd();
53559
- const result = deps.taskRunner ? await deps.taskRunner("generate_game_image", parameters, workspace) : await runTask(workspace, "generate_game_image", parameters, "mcp", { image: deps });
53684
+ validateImageToolInput(generateGameImageTool, args);
53685
+ const prompt = args.prompt.trim();
53686
+ if (!prompt) throw new Error(mcpT("image.missingPrompt"));
53687
+ const model = args.model ?? "asset-pro";
53688
+ const size = args.size ?? "2048x2048";
53689
+ if (!gameImageSizePresets(model).includes(size)) throw new Error(`${mcpT("image.invalidSize")}${model}: ${size}`);
53690
+ const background = args.background ?? "opaque";
53691
+ if (background === "auto" && model !== "asset-max") throw new Error(mcpT("image.autoBackgroundMax"));
53692
+ const input = normalizeGameImageInput({ prompt, imageModel: model, size, background });
53693
+ const parameters = { prompt, model, size: input.size, background: input.background };
53694
+ const result = await runImageToolTask("generate_game_image", parameters, args.workspace_dir?.trim() || deps.cwd(), deps);
53560
53695
  return await formatImageTaskResult(result, mcpT("image.completed"));
53561
53696
  } catch (error2) {
53562
53697
  return toolError(error2);
53563
53698
  }
53564
53699
  }
53565
- function buildGenerateInput(args) {
53566
- const input = {
53567
- prompt: args.prompt.trim(),
53568
- imageModel: "asset-lite"
53569
- };
53570
- if (args.image_type !== void 0) {
53571
- if (!GAME_IMAGE_TYPES.includes(args.image_type)) {
53572
- return new Error(mcpT("image.invalidType") + args.image_type);
53573
- }
53574
- input.imageType = args.image_type;
53575
- }
53576
- if (args.size !== void 0) {
53577
- input.size = args.size;
53578
- }
53579
- if (args.image_model !== void 0) {
53580
- if (!GAME_IMAGE_MODELS.includes(args.image_model)) {
53581
- return new Error(mcpT("image.invalidModel") + args.image_model);
53582
- }
53583
- input.imageModel = args.image_model;
53584
- }
53585
- if (args.background !== void 0) {
53586
- if (!GAME_IMAGE_BACKGROUNDS.includes(args.background)) {
53587
- return new Error(mcpT("image.invalidBackground") + args.background);
53588
- }
53589
- input.background = args.background;
53590
- }
53591
- if (args.reference_image_url !== void 0) {
53592
- const references = (Array.isArray(args.reference_image_url) ? args.reference_image_url : [args.reference_image_url]).map((value) => value.trim()).filter(Boolean);
53593
- if (!references.length) return new Error(mcpT("image.emptyReferences"));
53594
- input.referenceImageUrl = references.length === 1 ? references[0] : references;
53595
- }
53596
- return normalizeGameImageInput(input);
53597
- }
53598
53700
 
53599
53701
  // src/mcp/tools/cutoutGameImage.ts
53600
53702
  var CUTOUT_GAME_IMAGE_TOOL_NAME = "evomeme_cutout_game_image";
@@ -53605,33 +53707,19 @@ var cutoutGameImageTool = {
53605
53707
  inputSchema: {
53606
53708
  type: "object",
53607
53709
  properties: {
53608
- image_url: {
53609
- type: "string",
53610
- description: mcpT("cutout.imageUrl")
53611
- },
53612
- workspace_dir: {
53613
- type: "string",
53614
- description: mcpT("cutout.workspace")
53615
- }
53710
+ id: { type: "string", description: mcpT("imageTool.assetId") },
53711
+ image_url: { type: "string", description: mcpT("cutout.imageUrl") },
53712
+ workspace_dir: { type: "string", description: mcpT("image.workspace") }
53616
53713
  },
53617
- required: ["image_url"],
53714
+ oneOf: [{ required: ["id"] }, { required: ["image_url"] }],
53618
53715
  additionalProperties: false
53619
53716
  },
53620
- annotations: {
53621
- title: mcpT("cutout.title"),
53622
- destructiveHint: false,
53623
- idempotentHint: false,
53624
- openWorldHint: true,
53625
- readOnlyHint: false
53626
- }
53717
+ annotations: { title: mcpT("cutout.title"), destructiveHint: false, idempotentHint: false, openWorldHint: true, readOnlyHint: false }
53627
53718
  };
53628
- var defaultDeps8 = { ...defaultImageServiceDeps, taskRunner: runDetachedTask };
53629
- async function handleCutoutGameImage(args, deps = defaultDeps8) {
53719
+ async function handleCutoutGameImage(args, deps = defaultImageToolDeps) {
53630
53720
  try {
53631
- if (!args.image_url?.trim()) throw new Error(mcpT("cutout.missingImageUrl"));
53632
- const parameters = { image_url: args.image_url.trim() };
53633
- const workspace = args.workspace_dir?.trim() || deps.cwd();
53634
- const result = deps.taskRunner ? await deps.taskRunner("cutout_game_image", parameters, workspace) : await runTask(workspace, "cutout_game_image", parameters, "mcp", { image: deps });
53721
+ validateImageToolInput(cutoutGameImageTool, args);
53722
+ const result = await runImageToolTask("cutout_game_image", imageToolSource(args), args.workspace_dir?.trim() || deps.cwd(), deps);
53635
53723
  return await formatImageTaskResult(result, mcpT("cutout.completed"));
53636
53724
  } catch (error2) {
53637
53725
  return toolError(error2);
@@ -53685,8 +53773,8 @@ var generateGameVideoTool = {
53685
53773
  readOnlyHint: false
53686
53774
  }
53687
53775
  };
53688
- var defaultDeps9 = { ...defaultVideoGenerationDeps, taskRunner: runDetachedTask };
53689
- async function handleGenerateGameVideo(args, deps = defaultDeps9) {
53776
+ var defaultDeps8 = { ...defaultVideoGenerationDeps, taskRunner: runDetachedTask };
53777
+ async function handleGenerateGameVideo(args, deps = defaultDeps8) {
53690
53778
  try {
53691
53779
  const input = buildVideoInput(args);
53692
53780
  if (input instanceof Error) throw input;
@@ -53782,8 +53870,46 @@ function buildVideoInput(args) {
53782
53870
  return input;
53783
53871
  }
53784
53872
 
53873
+ // src/mcp/tools/colorKey.ts
53874
+ var COLOR_KEY_TOOL_NAME = "evomeme_color_key";
53875
+ var colorKeyTool = {
53876
+ name: COLOR_KEY_TOOL_NAME,
53877
+ description: mcpT("colorKey.description"),
53878
+ inputSchema: {
53879
+ type: "object",
53880
+ properties: {
53881
+ id: { type: "string", description: mcpT("imageTool.assetId") },
53882
+ color: { type: "string", default: "#ffffff", description: mcpT("colorKey.color") },
53883
+ strength: { type: "number", minimum: 0, maximum: 100, default: 80, description: mcpT("colorKey.strength") },
53884
+ contiguous_only: { type: "boolean", default: false, description: mcpT("colorKey.contiguousOnly") },
53885
+ despill: { type: "boolean", default: false, description: mcpT("colorKey.despill") },
53886
+ workspace_dir: { type: "string", description: mcpT("image.workspace") }
53887
+ },
53888
+ required: ["id"],
53889
+ additionalProperties: false
53890
+ },
53891
+ annotations: { title: mcpT("colorKey.title"), destructiveHint: false, idempotentHint: false, openWorldHint: false, readOnlyHint: false }
53892
+ };
53893
+ async function handleColorKey(args, deps = defaultImageToolDeps) {
53894
+ try {
53895
+ validateImageToolInput(colorKeyTool, args);
53896
+ if (!args.id.trim()) throw new Error(`${mcpT("imageTool.missingParameter")}id`);
53897
+ const parameters = {
53898
+ id: args.id.trim(),
53899
+ color: normalizeColorKeyHex(args.color ?? "#ffffff"),
53900
+ strength: args.strength ?? 80,
53901
+ contiguous_only: args.contiguous_only ?? false,
53902
+ despill: args.despill ?? false
53903
+ };
53904
+ const result = await runImageToolTask("color_key", parameters, args.workspace_dir?.trim() || deps.cwd(), deps);
53905
+ return await formatImageTaskResult(result, mcpT("colorKey.completed"));
53906
+ } catch (error2) {
53907
+ return toolError(error2);
53908
+ }
53909
+ }
53910
+
53785
53911
  // src/mcp/tools/stage/index.ts
53786
- import path58 from "node:path";
53912
+ import path59 from "node:path";
53787
53913
 
53788
53914
  // src/stage/index.ts
53789
53915
  init_schema();
@@ -53794,9 +53920,9 @@ init_sceneContract();
53794
53920
  init_workspace();
53795
53921
  init_schema();
53796
53922
  init_sceneContract();
53797
- import { copyFile as copyFile10, mkdir as mkdir26, writeFile as writeFile13, readFile as readFile22, realpath as realpath6, lstat as lstat3 } from "node:fs/promises";
53923
+ import { copyFile as copyFile10, mkdir as mkdir26, writeFile as writeFile13, readFile as readFile21, realpath as realpath6, lstat as lstat3 } from "node:fs/promises";
53798
53924
  import { existsSync as existsSync11 } from "node:fs";
53799
- import path57 from "node:path";
53925
+ import path58 from "node:path";
53800
53926
 
53801
53927
  // src/stage/runtime/applyCanonicalOrientation.ts
53802
53928
  function runtimeYawRadians(forwardAxis) {
@@ -53881,9 +54007,9 @@ init_publicDir();
53881
54007
  init_godotAppearance();
53882
54008
 
53883
54009
  // src/stage/threeDelivery.ts
53884
- import { readFile as readFile20 } from "node:fs/promises";
54010
+ import { readFile as readFile19 } from "node:fs/promises";
53885
54011
  import { createHash as createHash12 } from "node:crypto";
53886
- import path55 from "node:path";
54012
+ import path56 from "node:path";
53887
54013
  async function threeDeliveryFiles(publicDir, input, confirmation) {
53888
54014
  const runtimeFiles = input.runtimeFiles.filter((file) => ["scene-runtime.js", "three-appearance.js", "sceneContractRuntime.js"].includes(file) || file.startsWith("baked/") || file.startsWith("vendor/"));
53889
54015
  const packageFiles = /* @__PURE__ */ new Map(), gameFiles = /* @__PURE__ */ new Map();
@@ -53899,7 +54025,7 @@ async function threeDeliveryFiles(publicDir, input, confirmation) {
53899
54025
  };
53900
54026
  packageFiles.set("three.appearance.json", Buffer.from(JSON.stringify(config2, null, 2)));
53901
54027
  for (const file of runtimeFiles) {
53902
- const bytes = await readFile20(path55.join(publicDir, file));
54028
+ const bytes = await readFile19(path56.join(publicDir, file));
53903
54029
  if (createHash12("sha256").update(bytes).digest("hex") !== confirmation.implementation[file]) throw new Error("Three.js 显示实现已变化,请重新确认");
53904
54030
  packageFiles.set(`engine-runtime/${file}`, bytes);
53905
54031
  if (!file.startsWith("vendor/")) gameFiles.set(file, bytes);
@@ -53971,9 +54097,9 @@ init_workspace();
53971
54097
  init_schema();
53972
54098
  import { spawn as spawn9 } from "node:child_process";
53973
54099
  import { existsSync as existsSync10 } from "node:fs";
53974
- import { copyFile as copyFile9, mkdir as mkdir25, mkdtemp as mkdtemp7, readFile as readFile21, readdir as readdir6, realpath as realpath5, lstat as lstat2, rename as rename5, writeFile as writeFile12, open as open2, unlink as unlink8 } from "node:fs/promises";
54100
+ import { copyFile as copyFile9, mkdir as mkdir25, mkdtemp as mkdtemp7, readFile as readFile20, readdir as readdir6, realpath as realpath5, lstat as lstat2, rename as rename5, writeFile as writeFile12, open as open2, unlink as unlink8 } from "node:fs/promises";
53975
54101
  import { createHash as createHash13 } from "node:crypto";
53976
- import path56 from "node:path";
54102
+ import path57 from "node:path";
53977
54103
  init_publicDir();
53978
54104
  init_review();
53979
54105
  init_sceneContract();
@@ -53981,30 +54107,30 @@ init_godotAppearance();
53981
54107
  init_threeAppearance();
53982
54108
  var digest = (bytes) => ({ bytes: bytes.length, sha256: createHash13("sha256").update(bytes).digest("hex") });
53983
54109
  function resolveSceneExporter() {
53984
- const entry = path56.dirname(path56.resolve(process.argv[1] || process.cwd()));
54110
+ const entry = path57.dirname(path57.resolve(process.argv[1] || process.cwd()));
53985
54111
  const candidates = [
53986
- path56.join(entry, "stage-blender/export_scene.py"),
53987
- path56.join(entry, "../src/stage/blender/export_scene.py"),
53988
- path56.resolve("src/stage/blender/export_scene.py"),
53989
- path56.resolve("plugin_client/src/stage/blender/export_scene.py")
54112
+ path57.join(entry, "stage-blender/export_scene.py"),
54113
+ path57.join(entry, "../src/stage/blender/export_scene.py"),
54114
+ path57.resolve("src/stage/blender/export_scene.py"),
54115
+ path57.resolve("plugin_client/src/stage/blender/export_scene.py")
53990
54116
  ];
53991
54117
  const found = candidates.find((p) => existsSync10(p));
53992
54118
  if (!found) throw new Error("找不到 Blender 场景导出脚本,请重新构建或更新插件");
53993
54119
  return found;
53994
54120
  }
53995
54121
  async function readGodotProjectRenderer(projectDir) {
53996
- const text = await readFile21(path56.join(projectDir, "project.godot"), "utf8");
54122
+ const text = await readFile20(path57.join(projectDir, "project.godot"), "utf8");
53997
54123
  const section = text.match(/\[rendering\]([\s\S]*?)(?=\n\[|$)/)?.[1] || "";
53998
54124
  const value = section.match(/^renderer\/rendering_method\s*=\s*"([^"]+)"/m)?.[1] || (/^config_version\s*=/m.test(text) ? "forward_plus" : "gl_compatibility");
53999
54125
  if (!["gl_compatibility", "mobile", "forward_plus"].includes(value)) throw new Error("不支持的 Godot 工程渲染器:" + value);
54000
54126
  return value;
54001
54127
  }
54002
54128
  async function safeDirectory(workspace, directory) {
54003
- const relative = path56.relative(workspace, directory);
54004
- if (relative.startsWith("..") || path56.isAbsolute(relative)) throw new Error("场景导出目录必须位于工作区内");
54129
+ const relative = path57.relative(workspace, directory);
54130
+ if (relative.startsWith("..") || path57.isAbsolute(relative)) throw new Error("场景导出目录必须位于工作区内");
54005
54131
  let current = workspace;
54006
- for (const part of relative.split(path56.sep).filter(Boolean)) {
54007
- current = path56.join(current, part);
54132
+ for (const part of relative.split(path57.sep).filter(Boolean)) {
54133
+ current = path57.join(current, part);
54008
54134
  if (existsSync10(current)) {
54009
54135
  const stat12 = await lstat2(current);
54010
54136
  if (stat12.isSymbolicLink() || !stat12.isDirectory()) throw new Error("场景导出目录不能包含链接或文件:" + current);
@@ -54033,7 +54159,7 @@ async function nativeExport(command, args, logFile, timeout) {
54033
54159
  });
54034
54160
  }
54035
54161
  async function exportBlenderScene(input) {
54036
- if (!input.sourceBlendPath || !path56.isAbsolute(input.sourceBlendPath) || path56.extname(input.sourceBlendPath).toLowerCase() !== ".blend") throw new Error("source_blend_path 必须是已保存的 .blend 绝对路径");
54162
+ if (!input.sourceBlendPath || !path57.isAbsolute(input.sourceBlendPath) || path57.extname(input.sourceBlendPath).toLowerCase() !== ".blend") throw new Error("source_blend_path 必须是已保存的 .blend 绝对路径");
54037
54163
  const id = normalizeAssetId(input.assetId);
54038
54164
  if (!input.assetId) throw new Error("asset_id 必填");
54039
54165
  const width = input.width ?? 960, size = input.textureSize ?? 1024;
@@ -54044,24 +54170,24 @@ async function exportBlenderScene(input) {
54044
54170
  if (!Number.isInteger(width) || width < 320 || width > 1920) throw new Error("参考图宽度必须为 320–1920");
54045
54171
  if (![256, 512, 1024, 2048].includes(size)) throw new Error("烘焙贴图尺寸必须为 256/512/1024/2048");
54046
54172
  if (input.cameraNames && (!Array.isArray(input.cameraNames) || input.cameraNames.length > 6 || input.cameraNames.some((n) => typeof n !== "string" || !n))) throw new Error("camera_names 最多包含 6 个有效相机名");
54047
- const source = await realpath5(input.sourceBlendPath), sourceHash = digest(await readFile21(source));
54173
+ const source = await realpath5(input.sourceBlendPath), sourceHash = digest(await readFile20(source));
54048
54174
  await mkdir25(input.workspaceDir, { recursive: true });
54049
54175
  const workspace = await realpath5(input.workspaceDir), dest = packagePaths(workspace, id).packageDir;
54050
- await safeDirectory(workspace, path56.dirname(dest));
54176
+ await safeDirectory(workspace, path57.dirname(dest));
54051
54177
  if (existsSync10(dest)) {
54052
54178
  await safeDirectory(workspace, dest);
54053
54179
  if (!input.replaceExisting) throw new Error("资产包已存在;更新当前场景请设置 replace_existing=true");
54054
54180
  }
54055
- const jobRoot = path56.join(workspace, ".evomeme/scene-exports");
54181
+ const jobRoot = path57.join(workspace, ".evomeme/scene-exports");
54056
54182
  await safeDirectory(workspace, jobRoot);
54057
- const lockPath = path56.join(jobRoot, `${id}.lock`);
54183
+ const lockPath = path57.join(jobRoot, `${id}.lock`);
54058
54184
  const lock = await open2(lockPath, "wx").catch((error2) => {
54059
54185
  if (error2.code === "EEXIST") throw new Error("该资产正在导出;如上次进程意外终止,请确认其已退出后移除锁文件:" + lockPath);
54060
54186
  throw error2;
54061
54187
  });
54062
54188
  try {
54063
54189
  if (existsSync10(dest) && !input.replaceExisting) throw new Error("资产包已存在;更新当前场景请设置 replace_existing=true");
54064
- const job = await mkdtemp7(path56.join(jobRoot, "export-")), output = path56.join(job, "output");
54190
+ const job = await mkdtemp7(path57.join(jobRoot, "export-")), output = path57.join(job, "output");
54065
54191
  await mkdir25(output);
54066
54192
  const request = {
54067
54193
  output_dir: output,
@@ -54073,7 +54199,7 @@ async function exportBlenderScene(input) {
54073
54199
  bake_materials: input.bakeMaterials === true,
54074
54200
  texture_size: size
54075
54201
  };
54076
- const requestFile = path56.join(job, "request.json");
54202
+ const requestFile = path57.join(job, "request.json");
54077
54203
  await writeFile12(requestFile, JSON.stringify(request));
54078
54204
  await nativeExport(resolveBlenderExecutable(input.blenderExecutable), [
54079
54205
  "--background",
@@ -54086,12 +54212,12 @@ async function exportBlenderScene(input) {
54086
54212
  resolveSceneExporter(),
54087
54213
  "--",
54088
54214
  requestFile
54089
- ], path56.join(job, "blender.log"), input.timeoutMs ?? 9e5);
54090
- if (digest(await readFile21(source)).sha256 !== sourceHash.sha256) throw new Error("导出期间源 .blend 已变化,请保存后重试");
54091
- const contract = JSON.parse(await readFile21(path56.join(output, "export-manifest.json"), "utf8"));
54215
+ ], path57.join(job, "blender.log"), input.timeoutMs ?? 9e5);
54216
+ if (digest(await readFile20(source)).sha256 !== sourceHash.sha256) throw new Error("导出期间源 .blend 已变化,请保存后重试");
54217
+ const contract = JSON.parse(await readFile20(path57.join(output, "export-manifest.json"), "utf8"));
54092
54218
  const made = await createPackage({
54093
54219
  workspaceDir: job,
54094
- sourceGlbPath: path56.join(output, "model.glb"),
54220
+ sourceGlbPath: path57.join(output, "model.glb"),
54095
54221
  assetId: id,
54096
54222
  type: "scene",
54097
54223
  source: "blender",
@@ -54103,23 +54229,23 @@ async function exportBlenderScene(input) {
54103
54229
  });
54104
54230
  const files = {};
54105
54231
  async function copyArtifacts(relative = "") {
54106
- for (const entry of await readdir6(path56.join(output, relative), { withFileTypes: true })) {
54232
+ for (const entry of await readdir6(path57.join(output, relative), { withFileTypes: true })) {
54107
54233
  const file = relative ? `${relative}/${entry.name}` : entry.name;
54108
54234
  if (entry.isSymbolicLink()) throw new Error("导出产物不允许符号链接");
54109
54235
  if (entry.isDirectory()) {
54110
- await mkdir25(path56.join(made.paths.packageDir, file), { recursive: true });
54236
+ await mkdir25(path57.join(made.paths.packageDir, file), { recursive: true });
54111
54237
  await copyArtifacts(file);
54112
54238
  } else if (entry.isFile() && file !== "export-manifest.json" && !file.endsWith(".blend1")) {
54113
- const bytes = await readFile21(path56.join(output, file));
54114
- await copyFile9(path56.join(output, file), path56.join(made.paths.packageDir, file));
54239
+ const bytes = await readFile20(path57.join(output, file));
54240
+ await copyFile9(path57.join(output, file), path57.join(made.paths.packageDir, file));
54115
54241
  files[file] = digest(bytes);
54116
54242
  }
54117
54243
  }
54118
54244
  }
54119
54245
  await copyArtifacts();
54120
54246
  async function previousProfile(file, schema) {
54121
- if (!existsSync10(path56.join(dest, file))) return {};
54122
- const profile = JSON.parse(await readFile21(path56.join(dest, file), "utf8"));
54247
+ if (!existsSync10(path57.join(dest, file))) return {};
54248
+ const profile = JSON.parse(await readFile20(path57.join(dest, file), "utf8"));
54123
54249
  return profile.schema === schema ? profile : {};
54124
54250
  }
54125
54251
  const oldGodot = await previousProfile("runtime/godot.profile.json", "evomeme.godot_scene_profile.v1");
@@ -54130,17 +54256,17 @@ async function exportBlenderScene(input) {
54130
54256
  buildThreeProfile(id, contract, three);
54131
54257
  for (const [file, profile] of [["runtime/godot.profile.json", godot], ["runtime/three.profile.json", three]]) {
54132
54258
  const bytes = Buffer.from(JSON.stringify(profile, null, 2));
54133
- await writeFile12(path56.join(made.paths.packageDir, file), bytes);
54259
+ await writeFile12(path57.join(made.paths.packageDir, file), bytes);
54134
54260
  files[file] = digest(bytes);
54135
54261
  }
54136
54262
  contract.files = files;
54137
54263
  contract.display_profiles = { three_js: { profile_file: "runtime/three.profile.json" }, godot: { profile_file: "runtime/godot.profile.json" } };
54138
- await writeFile12(path56.join(made.paths.packageDir, "scene.contract.json"), JSON.stringify(contract, null, 2));
54264
+ await writeFile12(path57.join(made.paths.packageDir, "scene.contract.json"), JSON.stringify(contract, null, 2));
54139
54265
  await readSceneSupplement(made.paths.packageDir, made.asset);
54140
54266
  const check2 = await inspectAsset(job, id, resolveStagePublicDir());
54141
54267
  if (!check2.technical_ok) throw new Error("导出 GLB 未通过格式验证;原资产包保持不变。诊断目录:" + job);
54142
- await safeDirectory(workspace, path56.dirname(dest));
54143
- const backup = existsSync10(dest) ? path56.join(job, "previous-package") : void 0;
54268
+ await safeDirectory(workspace, path57.dirname(dest));
54269
+ const backup = existsSync10(dest) ? path57.join(job, "previous-package") : void 0;
54144
54270
  if (backup) {
54145
54271
  await safeDirectory(workspace, dest);
54146
54272
  await rename5(dest, backup);
@@ -54154,9 +54280,9 @@ async function exportBlenderScene(input) {
54154
54280
  return {
54155
54281
  asset_id: id,
54156
54282
  package_dir: dest,
54157
- source_blend_path: path56.join(dest, "source/scene.blend"),
54158
- model_path: path56.join(dest, "model.glb"),
54159
- contract_path: path56.join(dest, "scene.contract.json"),
54283
+ source_blend_path: path57.join(dest, "source/scene.blend"),
54284
+ model_path: path57.join(dest, "model.glb"),
54285
+ contract_path: path57.join(dest, "scene.contract.json"),
54160
54286
  backup_dir: backup,
54161
54287
  job_dir: job,
54162
54288
  adaptation: check2.adaptation,
@@ -54171,16 +54297,16 @@ async function exportBlenderScene(input) {
54171
54297
  // src/stage/syncEngine.ts
54172
54298
  function destRoot(projectDir, target) {
54173
54299
  if (target === "godot") {
54174
- return path57.join(projectDir, "assets", "canonical");
54300
+ return path58.join(projectDir, "assets", "canonical");
54175
54301
  }
54176
- return path57.join(projectDir, "public", "assets", "canonical");
54302
+ return path58.join(projectDir, "public", "assets", "canonical");
54177
54303
  }
54178
54304
  async function syncPackageToEngine(input) {
54179
- const projectDir = path57.resolve(input.projectDir);
54305
+ const projectDir = path58.resolve(input.projectDir);
54180
54306
  if (!existsSync11(projectDir)) {
54181
54307
  throw new Error(`引擎工程目录不存在:${projectDir}`);
54182
54308
  }
54183
- if (input.target === "godot" && !existsSync11(path57.join(projectDir, "project.godot"))) {
54309
+ if (input.target === "godot" && !existsSync11(path58.join(projectDir, "project.godot"))) {
54184
54310
  throw new Error(`Godot 工程缺少 project.godot:${projectDir}`);
54185
54311
  }
54186
54312
  const exported = await exportPackage(input.workspaceDir, input.assetId);
@@ -54189,12 +54315,12 @@ async function syncPackageToEngine(input) {
54189
54315
  const godotAppearance = input.target === "godot" ? await godotAppearanceInput(await reviewInputs(input.workspaceDir, input.assetId, publicDir), publicDir) : null;
54190
54316
  if (godotAppearance && await readGodotProjectRenderer(projectDir) !== godotAppearance.profile.renderer) throw new Error("Godot 工程渲染器与 Stage 配置不同;更新 profile 并重新查看确认后再同步。");
54191
54317
  const threeDelivery = input.target === "three_js" ? await threeDeliveryFiles(publicDir, await reviewInputs(input.workspaceDir, input.assetId, publicDir), confirmation) : null;
54192
- const gameRuntimeDir = path57.join(projectDir, "src", "evomeme-stage", exported.asset.asset_id);
54318
+ const gameRuntimeDir = path58.join(projectDir, "src", "evomeme-stage", exported.asset.asset_id);
54193
54319
  const root = destRoot(projectDir, input.target);
54194
- const destPackageDir = path57.join(root, exported.asset.asset_id);
54320
+ const destPackageDir = path58.join(root, exported.asset.asset_id);
54195
54321
  const hasSupplement = exported.files.includes(SCENE_CONTRACT_FILENAME);
54196
54322
  const packageFiles = hasSupplement ? exported.files : [MODEL_FILENAME, ASSET_JSON_FILENAME];
54197
- if (!hasSupplement && existsSync11(path57.join(destPackageDir, SCENE_CONTRACT_FILENAME))) throw new Error("目标保留了旧场景契约,不能用不含契约的包覆盖;请先明确清理目标旧包");
54323
+ if (!hasSupplement && existsSync11(path58.join(destPackageDir, SCENE_CONTRACT_FILENAME))) throw new Error("目标保留了旧场景契约,不能用不含契约的包覆盖;请先明确清理目标旧包");
54198
54324
  const generatedFiles = [input.target === "three_js" ? "applyCanonicalOrientation.example.ts" : "canonical_loader.gd"];
54199
54325
  if (input.target === "three_js") generatedFiles.push("applyCanonicalOrientation.ts");
54200
54326
  else generatedFiles.push("canonical_appearance.gd", "godot.appearance.json", "GODOT_IMPORT.md");
@@ -54211,26 +54337,26 @@ async function syncPackageToEngine(input) {
54211
54337
  if (cursor === destination && !entry.isFile()) throw new Error(`同步目标不是普通文件:${cursor}`);
54212
54338
  if (entry.isSymbolicLink()) throw new Error(`同步目标不可包含符号链接或 junction:${cursor}`);
54213
54339
  const resolved = await realpath6(cursor);
54214
- const relative = path57.relative(actualProject, resolved);
54215
- if (relative === ".." || relative.startsWith(`..${path57.sep}`) || path57.isAbsolute(relative)) throw new Error(`同步目标越出引擎工程:${cursor}`);
54340
+ const relative = path58.relative(actualProject, resolved);
54341
+ if (relative === ".." || relative.startsWith(`..${path58.sep}`) || path58.isAbsolute(relative)) throw new Error(`同步目标越出引擎工程:${cursor}`);
54216
54342
  } catch (error2) {
54217
54343
  if (error2.code !== "ENOENT") throw error2;
54218
54344
  }
54219
- const parent = path57.dirname(cursor);
54345
+ const parent = path58.dirname(cursor);
54220
54346
  if (parent === cursor) throw new Error("同步目标不在引擎工程内");
54221
54347
  cursor = parent;
54222
54348
  }
54223
54349
  }
54224
- for (const file of [...packageFiles, ...generatedFiles]) await checkDestination(path57.join(destPackageDir, file));
54225
- if (threeDelivery) for (const file of threeDelivery.gameFiles.keys()) await checkDestination(path57.join(gameRuntimeDir, file));
54226
- await checkDestination(path57.join(root, "index.json"));
54227
- if (await realpath6(exported.paths.packageDir) === path57.resolve(destPackageDir)) throw new Error("源资产包与引擎目标目录相同");
54350
+ for (const file of [...packageFiles, ...generatedFiles]) await checkDestination(path58.join(destPackageDir, file));
54351
+ if (threeDelivery) for (const file of threeDelivery.gameFiles.keys()) await checkDestination(path58.join(gameRuntimeDir, file));
54352
+ await checkDestination(path58.join(root, "index.json"));
54353
+ if (await realpath6(exported.paths.packageDir) === path58.resolve(destPackageDir)) throw new Error("源资产包与引擎目标目录相同");
54228
54354
  await mkdir26(destPackageDir, { recursive: true });
54229
- const destModel = path57.join(destPackageDir, MODEL_FILENAME);
54230
- const destJson = path57.join(destPackageDir, ASSET_JSON_FILENAME);
54355
+ const destModel = path58.join(destPackageDir, MODEL_FILENAME);
54356
+ const destJson = path58.join(destPackageDir, ASSET_JSON_FILENAME);
54231
54357
  for (const file of packageFiles) {
54232
- await mkdir26(path57.dirname(path57.join(destPackageDir, file)), { recursive: true });
54233
- await copyFile10(path57.join(exported.paths.packageDir, file), path57.join(destPackageDir, file));
54358
+ await mkdir26(path58.dirname(path58.join(destPackageDir, file)), { recursive: true });
54359
+ await copyFile10(path58.join(exported.paths.packageDir, file), path58.join(destPackageDir, file));
54234
54360
  }
54235
54361
  const notes = [
54236
54362
  `已同步到 ${destPackageDir}`,
@@ -54239,7 +54365,7 @@ async function syncPackageToEngine(input) {
54239
54365
  if (hasSupplement) notes.push("已验证并同步 scene.contract.json 及全部声明依赖。相机须按契约画幅留边;外观 profile 由引擎显式消费,不代表跨渲染器像素一致。");
54240
54366
  let helperPath;
54241
54367
  if (input.target === "three_js") {
54242
- const orientationPath = path57.join(destPackageDir, "applyCanonicalOrientation.ts");
54368
+ const orientationPath = path58.join(destPackageDir, "applyCanonicalOrientation.ts");
54243
54369
  await writeFile13(orientationPath, `// @ts-nocheck
54244
54370
  // Generated from the canonical runtime helper.
54245
54371
  ${runtimeYawRadians.toString()}
@@ -54247,7 +54373,7 @@ ${degToRad.toString()}
54247
54373
  ${applyCanonicalOrientation.toString()}
54248
54374
  export { ${runtimeYawRadians.name} as runtimeYawRadians, ${degToRad.name} as degToRad, ${applyCanonicalOrientation.name} as applyCanonicalOrientation };
54249
54375
  `, "utf8");
54250
- helperPath = path57.join(destPackageDir, "applyCanonicalOrientation.example.ts");
54376
+ helperPath = path58.join(destPackageDir, "applyCanonicalOrientation.example.ts");
54251
54377
  await writeFile13(
54252
54378
  helperPath,
54253
54379
  [
@@ -54277,19 +54403,19 @@ export { ${runtimeYawRadians.name} as runtimeYawRadians, ${degToRad.name} as deg
54277
54403
  notes.push(`three 示例加载器:${helperPath}`);
54278
54404
  notes.push(`URL:/assets/canonical/${exported.asset.asset_id}/model.glb`);
54279
54405
  for (const [file, bytes] of threeDelivery.packageFiles) {
54280
- await mkdir26(path57.dirname(path57.join(destPackageDir, file)), { recursive: true });
54281
- await writeFile13(path57.join(destPackageDir, file), bytes);
54406
+ await mkdir26(path58.dirname(path58.join(destPackageDir, file)), { recursive: true });
54407
+ await writeFile13(path58.join(destPackageDir, file), bytes);
54282
54408
  }
54283
54409
  for (const [file, bytes] of threeDelivery.gameFiles) {
54284
- await mkdir26(path57.dirname(path57.join(gameRuntimeDir, file)), { recursive: true });
54285
- await writeFile13(path57.join(gameRuntimeDir, file), bytes);
54410
+ await mkdir26(path58.dirname(path58.join(gameRuntimeDir, file)), { recursive: true });
54411
+ await writeFile13(path58.join(gameRuntimeDir, file), bytes);
54286
54412
  }
54287
- helperPath = path57.join(gameRuntimeDir, "canonical_loader.js");
54413
+ helperPath = path58.join(gameRuntimeDir, "canonical_loader.js");
54288
54414
  notes.push("完整场景使用 loadReviewedScene,并通过返回的 render(camera, delta) 执行同一套灯光、环境、烘焙和后处理。");
54289
54415
  notes.push("Three.js 版本锁定为 0.170.0;游戏入口:" + helperPath);
54290
54416
  } else {
54291
- for (const file of authoringFiles) await writeFile13(path57.join(destPackageDir, `${file}.import`), '[remap]\nimporter="keep"\n');
54292
- helperPath = path57.join(destPackageDir, "canonical_loader.gd");
54417
+ for (const file of authoringFiles) await writeFile13(path58.join(destPackageDir, `${file}.import`), '[remap]\nimporter="keep"\n');
54418
+ helperPath = path58.join(destPackageDir, "canonical_loader.gd");
54293
54419
  await writeFile13(
54294
54420
  helperPath,
54295
54421
  [
@@ -54372,14 +54498,14 @@ export { ${runtimeYawRadians.name} as runtimeYawRadians, ${degToRad.name} as deg
54372
54498
  notes.push(`Godot 加载脚本:${helperPath}`);
54373
54499
  notes.push("Godot 同步门禁已验证 alignment.ready、源/意图/配方修订与完整 TRS。");
54374
54500
  notes.push(`res 路径:res://assets/canonical/${exported.asset.asset_id}/`);
54375
- await writeFile13(path57.join(destPackageDir, "canonical_appearance.gd"), godotAppearance.runtime);
54376
- await writeFile13(path57.join(destPackageDir, "godot.appearance.json"), JSON.stringify({
54501
+ await writeFile13(path58.join(destPackageDir, "canonical_appearance.gd"), godotAppearance.runtime);
54502
+ await writeFile13(path58.join(destPackageDir, "godot.appearance.json"), JSON.stringify({
54377
54503
  profile: godotAppearance.profile,
54378
54504
  profile_key: godotAppearance.key,
54379
54505
  camera: confirmation.presentation.camera,
54380
54506
  stage_revision: confirmation.revision
54381
54507
  }, null, 2));
54382
- const loader = await readFile22(helperPath, "utf8");
54508
+ const loader = await readFile21(helperPath, "utf8");
54383
54509
  await writeFile13(helperPath, loader + "\n" + [
54384
54510
  "## Scene entry: reproduce Stage-reviewed materials, lights, environment and camera.",
54385
54511
  "## Call once for a scene. For props in an existing lit level, use load_package.",
@@ -54395,7 +54521,7 @@ export { ${runtimeYawRadians.name} as runtimeYawRadians, ${degToRad.name} as deg
54395
54521
  " return model",
54396
54522
  ""
54397
54523
  ].join("\n"));
54398
- await writeFile13(path57.join(destPackageDir, "GODOT_IMPORT.md"), `# Godot 已确认场景
54524
+ await writeFile13(path58.join(destPackageDir, "GODOT_IMPORT.md"), `# Godot 已确认场景
54399
54525
 
54400
54526
  Stage 版本:${confirmation.revision}
54401
54527
 
@@ -54412,11 +54538,11 @@ ${godotAppearance.profile.warnings.join("\n")}
54412
54538
  notes.push("已通过 Stage 当前版本 Godot 原生画面确认;使用 load_scene 复现基础材质、灯光、环境和相机。");
54413
54539
  notes.push(...godotAppearance.profile.warnings);
54414
54540
  }
54415
- const indexPath = path57.join(root, "index.json");
54541
+ const indexPath = path58.join(root, "index.json");
54416
54542
  let index = { assets: {} };
54417
54543
  if (existsSync11(indexPath)) {
54418
54544
  try {
54419
- index = JSON.parse(await readFile22(indexPath, "utf8"));
54545
+ index = JSON.parse(await readFile21(indexPath, "utf8"));
54420
54546
  if (!index.assets) index.assets = {};
54421
54547
  } catch {
54422
54548
  index = { assets: {} };
@@ -54433,9 +54559,9 @@ ${godotAppearance.profile.warnings.join("\n")}
54433
54559
  model_path: destModel,
54434
54560
  asset_json_path: destJson,
54435
54561
  helper_path: helperPath,
54436
- scene_contract_path: hasSupplement ? path57.join(destPackageDir, SCENE_CONTRACT_FILENAME) : void 0,
54562
+ scene_contract_path: hasSupplement ? path58.join(destPackageDir, SCENE_CONTRACT_FILENAME) : void 0,
54437
54563
  synced_files: [...packageFiles, ...generatedFiles],
54438
- game_source_files: threeDelivery ? [...threeDelivery.gameFiles.keys()].map((file) => path57.join(gameRuntimeDir, file)) : void 0,
54564
+ game_source_files: threeDelivery ? [...threeDelivery.gameFiles.keys()].map((file) => path58.join(gameRuntimeDir, file)) : void 0,
54439
54565
  alignment_status: exported.asset.alignment.overall_status,
54440
54566
  alignment_key: exported.asset.alignment.revisions.alignment_key,
54441
54567
  notes
@@ -54731,7 +54857,7 @@ function listStageTools() {
54731
54857
  function resolveWorkspace2(raw, cwd) {
54732
54858
  const value = String(raw || "").trim();
54733
54859
  if (!value) throw new Error("workspace_dir 必填");
54734
- return path58.resolve(value.startsWith("/") || /^[A-Za-z]:/.test(value) ? value : path58.join(cwd, value));
54860
+ return path59.resolve(value.startsWith("/") || /^[A-Za-z]:/.test(value) ? value : path59.join(cwd, value));
54735
54861
  }
54736
54862
  async function handleStageOpen(args, cwd = process.cwd()) {
54737
54863
  try {
@@ -54759,7 +54885,7 @@ async function handleStageOpen(args, cwd = process.cwd()) {
54759
54885
  async function handleAssetPackageCreate(args, cwd = process.cwd()) {
54760
54886
  try {
54761
54887
  const workspaceDir = resolveWorkspace2(args.workspace_dir, cwd);
54762
- const sourceGlbPath = path58.resolve(String(args.source_glb_path || ""));
54888
+ const sourceGlbPath = path59.resolve(String(args.source_glb_path || ""));
54763
54889
  const result = await createPackage({
54764
54890
  workspaceDir,
54765
54891
  sourceGlbPath,
@@ -54915,7 +55041,7 @@ async function handleAssetRepairBlender(args, cwd = process.cwd()) {
54915
55041
  async function handleAssetSyncThree(args, cwd = process.cwd()) {
54916
55042
  try {
54917
55043
  const workspaceDir = resolveWorkspace2(args.workspace_dir, cwd);
54918
- const projectDir = path58.resolve(String(args.project_dir || ""));
55044
+ const projectDir = path59.resolve(String(args.project_dir || ""));
54919
55045
  const result = await syncPackageToEngine({
54920
55046
  workspaceDir,
54921
55047
  assetId: String(args.asset_id || ""),
@@ -54930,7 +55056,7 @@ async function handleAssetSyncThree(args, cwd = process.cwd()) {
54930
55056
  async function handleAssetSyncGodot(args, cwd = process.cwd()) {
54931
55057
  try {
54932
55058
  const workspaceDir = resolveWorkspace2(args.workspace_dir, cwd);
54933
- const projectDir = path58.resolve(String(args.project_dir || ""));
55059
+ const projectDir = path59.resolve(String(args.project_dir || ""));
54934
55060
  const result = await syncPackageToEngine({
54935
55061
  workspaceDir,
54936
55062
  assetId: String(args.asset_id || ""),
@@ -55003,10 +55129,12 @@ function listMcpTools() {
55003
55129
  return [
55004
55130
  checkEvomemeLoginTool,
55005
55131
  loginEvomemeTool,
55132
+ reinitProjectTool,
55006
55133
  submitGameCodeTool,
55007
55134
  uploadGameAssetTool,
55008
55135
  generateGameImageTool,
55009
55136
  cutoutGameImageTool,
55137
+ colorKeyTool,
55010
55138
  generateGameVideoTool,
55011
55139
  // ...listMeshTools(),
55012
55140
  ...listStageTools()
@@ -55022,14 +55150,21 @@ async function dispatchToolCall(name, rawArgs = {}) {
55022
55150
  if (name === SUBMIT_GAME_CODE_TOOL_NAME) {
55023
55151
  return handleSubmitGameCode(parseSubmitArgs(rawArgs));
55024
55152
  }
55153
+ if (name === REINIT_PROJECT_TOOL_NAME) {
55154
+ const { workspace_dir, name: projectName } = parseSubmitArgs(rawArgs);
55155
+ return handleReinitProject({ workspace_dir, name: projectName });
55156
+ }
55025
55157
  if (name === UPLOAD_GAME_ASSET_TOOL_NAME) {
55026
55158
  return handleUploadGameAsset(parseUploadArgs(rawArgs));
55027
55159
  }
55028
55160
  if (name === GENERATE_GAME_IMAGE_TOOL_NAME) {
55029
- return handleGenerateGameImage(parseGenerateImageArgs(rawArgs));
55161
+ return handleGenerateGameImage(rawArgs);
55030
55162
  }
55031
55163
  if (name === CUTOUT_GAME_IMAGE_TOOL_NAME) {
55032
- return handleCutoutGameImage(parseCutoutImageArgs(rawArgs));
55164
+ return handleCutoutGameImage(rawArgs);
55165
+ }
55166
+ if (name === COLOR_KEY_TOOL_NAME) {
55167
+ return handleColorKey(rawArgs);
55033
55168
  }
55034
55169
  if (name === GENERATE_GAME_VIDEO_TOOL_NAME) {
55035
55170
  return handleGenerateGameVideo(parseGenerateVideoArgs(rawArgs));
@@ -55071,23 +55206,6 @@ function parseUploadArgs(args) {
55071
55206
  extension: typeof args.extension === "string" ? args.extension : void 0
55072
55207
  };
55073
55208
  }
55074
- function parseGenerateImageArgs(args) {
55075
- return {
55076
- prompt: typeof args.prompt === "string" ? args.prompt : "",
55077
- image_type: typeof args.image_type === "string" ? args.image_type : void 0,
55078
- size: typeof args.size === "string" ? args.size : void 0,
55079
- reference_image_url: typeof args.reference_image_url === "string" ? args.reference_image_url : Array.isArray(args.reference_image_url) ? args.reference_image_url.filter((value) => typeof value === "string") : void 0,
55080
- image_model: typeof args.image_model === "string" ? args.image_model : void 0,
55081
- background: typeof args.background === "string" ? args.background : void 0,
55082
- workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
55083
- };
55084
- }
55085
- function parseCutoutImageArgs(args) {
55086
- return {
55087
- image_url: typeof args.image_url === "string" ? args.image_url : "",
55088
- workspace_dir: typeof args.workspace_dir === "string" ? args.workspace_dir : void 0
55089
- };
55090
- }
55091
55209
  function parseGenerateVideoArgs(args) {
55092
55210
  const references = Array.isArray(args.references) ? args.references.filter((value) => Boolean(value && typeof value === "object")).map((value) => ({
55093
55211
  type: typeof value.type === "string" ? value.type : void 0,