@fangyb/ahchat-bridge 0.1.32 → 0.1.34

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.
@@ -3258,8 +3258,8 @@ var require_utils = __commonJS({
3258
3258
  }
3259
3259
  return ind;
3260
3260
  }
3261
- function removeDotSegments(path) {
3262
- let input = path;
3261
+ function removeDotSegments(path2) {
3262
+ let input = path2;
3263
3263
  const output = [];
3264
3264
  let nextSlash = -1;
3265
3265
  let len = 0;
@@ -3512,8 +3512,8 @@ var require_schemes = __commonJS({
3512
3512
  wsComponent.secure = void 0;
3513
3513
  }
3514
3514
  if (wsComponent.resourceName) {
3515
- const [path, query] = wsComponent.resourceName.split("?");
3516
- wsComponent.path = path && path !== "/" ? path : void 0;
3515
+ const [path2, query] = wsComponent.resourceName.split("?");
3516
+ wsComponent.path = path2 && path2 !== "/" ? path2 : void 0;
3517
3517
  wsComponent.query = query;
3518
3518
  wsComponent.resourceName = void 0;
3519
3519
  }
@@ -6952,12 +6952,12 @@ var require_dist = __commonJS({
6952
6952
  throw new Error(`Unknown format "${name}"`);
6953
6953
  return f;
6954
6954
  };
6955
- function addFormats(ajv, list, fs, exportName) {
6955
+ function addFormats(ajv, list, fs2, exportName) {
6956
6956
  var _a3;
6957
6957
  var _b;
6958
6958
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
6959
6959
  for (const f of list)
6960
- ajv.addFormat(f, fs[f]);
6960
+ ajv.addFormat(f, fs2[f]);
6961
6961
  }
6962
6962
  module2.exports = exports2 = formatsPlugin;
6963
6963
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -7350,8 +7350,8 @@ function getErrorMap() {
7350
7350
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v3/helpers/parseUtil.js
7351
7351
  init_cjs_shims();
7352
7352
  var makeIssue = (params) => {
7353
- const { data, path, errorMaps, issueData } = params;
7354
- const fullPath = [...path, ...issueData.path || []];
7353
+ const { data, path: path2, errorMaps, issueData } = params;
7354
+ const fullPath = [...path2, ...issueData.path || []];
7355
7355
  const fullIssue = {
7356
7356
  ...issueData,
7357
7357
  path: fullPath
@@ -7470,11 +7470,11 @@ var errorUtil;
7470
7470
 
7471
7471
  // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v3/types.js
7472
7472
  var ParseInputLazyPath = class {
7473
- constructor(parent, value, path, key) {
7473
+ constructor(parent, value, path2, key) {
7474
7474
  this._cachedPath = [];
7475
7475
  this.parent = parent;
7476
7476
  this.data = value;
7477
- this._path = path;
7477
+ this._path = path2;
7478
7478
  this._key = key;
7479
7479
  }
7480
7480
  get path() {
@@ -11406,10 +11406,10 @@ function mergeDefs(...defs) {
11406
11406
  function cloneDef(schema) {
11407
11407
  return mergeDefs(schema._zod.def);
11408
11408
  }
11409
- function getElementAtPath(obj, path) {
11410
- if (!path)
11409
+ function getElementAtPath(obj, path2) {
11410
+ if (!path2)
11411
11411
  return obj;
11412
- return path.reduce((acc, key) => acc?.[key], obj);
11412
+ return path2.reduce((acc, key) => acc?.[key], obj);
11413
11413
  }
11414
11414
  function promiseAllObject(promisesObj) {
11415
11415
  const keys = Object.keys(promisesObj);
@@ -11818,11 +11818,11 @@ function explicitlyAborted(x, startIndex = 0) {
11818
11818
  }
11819
11819
  return false;
11820
11820
  }
11821
- function prefixIssues(path, issues) {
11821
+ function prefixIssues(path2, issues) {
11822
11822
  return issues.map((iss) => {
11823
11823
  var _a3;
11824
11824
  (_a3 = iss).path ?? (_a3.path = []);
11825
- iss.path.unshift(path);
11825
+ iss.path.unshift(path2);
11826
11826
  return iss;
11827
11827
  });
11828
11828
  }
@@ -11969,16 +11969,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
11969
11969
  }
11970
11970
  function formatError(error51, mapper = (issue2) => issue2.message) {
11971
11971
  const fieldErrors = { _errors: [] };
11972
- const processError = (error52, path = []) => {
11972
+ const processError = (error52, path2 = []) => {
11973
11973
  for (const issue2 of error52.issues) {
11974
11974
  if (issue2.code === "invalid_union" && issue2.errors.length) {
11975
- issue2.errors.map((issues) => processError({ issues }, [...path, ...issue2.path]));
11975
+ issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
11976
11976
  } else if (issue2.code === "invalid_key") {
11977
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
11977
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
11978
11978
  } else if (issue2.code === "invalid_element") {
11979
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
11979
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
11980
11980
  } else {
11981
- const fullpath = [...path, ...issue2.path];
11981
+ const fullpath = [...path2, ...issue2.path];
11982
11982
  if (fullpath.length === 0) {
11983
11983
  fieldErrors._errors.push(mapper(issue2));
11984
11984
  } else {
@@ -12005,17 +12005,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
12005
12005
  }
12006
12006
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
12007
12007
  const result = { errors: [] };
12008
- const processError = (error52, path = []) => {
12008
+ const processError = (error52, path2 = []) => {
12009
12009
  var _a3, _b;
12010
12010
  for (const issue2 of error52.issues) {
12011
12011
  if (issue2.code === "invalid_union" && issue2.errors.length) {
12012
- issue2.errors.map((issues) => processError({ issues }, [...path, ...issue2.path]));
12012
+ issue2.errors.map((issues) => processError({ issues }, [...path2, ...issue2.path]));
12013
12013
  } else if (issue2.code === "invalid_key") {
12014
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
12014
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
12015
12015
  } else if (issue2.code === "invalid_element") {
12016
- processError({ issues: issue2.issues }, [...path, ...issue2.path]);
12016
+ processError({ issues: issue2.issues }, [...path2, ...issue2.path]);
12017
12017
  } else {
12018
- const fullpath = [...path, ...issue2.path];
12018
+ const fullpath = [...path2, ...issue2.path];
12019
12019
  if (fullpath.length === 0) {
12020
12020
  result.errors.push(mapper(issue2));
12021
12021
  continue;
@@ -12047,8 +12047,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
12047
12047
  }
12048
12048
  function toDotPath(_path) {
12049
12049
  const segs = [];
12050
- const path = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12051
- for (const seg of path) {
12050
+ const path2 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
12051
+ for (const seg of path2) {
12052
12052
  if (typeof seg === "number")
12053
12053
  segs.push(`[${seg}]`);
12054
12054
  else if (typeof seg === "symbol")
@@ -25262,13 +25262,13 @@ function resolveRef(ref, ctx) {
25262
25262
  if (!ref.startsWith("#")) {
25263
25263
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
25264
25264
  }
25265
- const path = ref.slice(1).split("/").filter(Boolean);
25266
- if (path.length === 0) {
25265
+ const path2 = ref.slice(1).split("/").filter(Boolean);
25266
+ if (path2.length === 0) {
25267
25267
  return ctx.rootSchema;
25268
25268
  }
25269
25269
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
25270
- if (path[0] === defsKey) {
25271
- const key = path[1];
25270
+ if (path2[0] === defsKey) {
25271
+ const key = path2[1];
25272
25272
  if (!key || !ctx.defs[key]) {
25273
25273
  throw new Error(`Reference not found: ${ref}`);
25274
25274
  }
@@ -31231,6 +31231,63 @@ var StdioServerTransport = class {
31231
31231
  }
31232
31232
  };
31233
31233
 
31234
+ // src/seedanceMcpCli.ts
31235
+ var import_promises = __toESM(require("fs/promises"), 1);
31236
+ var import_node_path = __toESM(require("path"), 1);
31237
+
31238
+ // src/officialMcpQuota.ts
31239
+ init_cjs_shims();
31240
+ var OfficialMcpQuotaError = class extends Error {
31241
+ constructor(message) {
31242
+ super(message);
31243
+ this.name = "OfficialMcpQuotaError";
31244
+ }
31245
+ };
31246
+ async function consumeOfficialMcpDailyQuota(serverName, toolName) {
31247
+ const serverApiUrl = process.env.AHCHAT_SERVER_API_URL?.trim();
31248
+ const bridgeToken = process.env.AHCHAT_BRIDGE_TOKEN?.trim();
31249
+ if (!serverApiUrl || !bridgeToken) return;
31250
+ let response;
31251
+ try {
31252
+ response = await fetch(`${serverApiUrl.replace(/\/+$/, "")}/api/mcp/usage/consume`, {
31253
+ method: "POST",
31254
+ headers: {
31255
+ "Content-Type": "application/json",
31256
+ "X-AHChat-Bridge-Token": bridgeToken
31257
+ },
31258
+ body: JSON.stringify({ serverName, toolName })
31259
+ });
31260
+ } catch {
31261
+ throw new OfficialMcpQuotaError("\u6682\u65F6\u65E0\u6CD5\u6821\u9A8C\u5B98\u65B9\u5A92\u4F53 MCP \u6BCF\u65E5\u989D\u5EA6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002");
31262
+ }
31263
+ if (response.status === 404) return;
31264
+ const body = await readQuotaResponse(response);
31265
+ if (response.status === 429 || body.allowed === false) {
31266
+ throw new OfficialMcpQuotaError(formatQuotaExceededMessage(serverName, body));
31267
+ }
31268
+ if (!response.ok) {
31269
+ throw new OfficialMcpQuotaError(body.message || "\u5B98\u65B9\u5A92\u4F53 MCP \u6BCF\u65E5\u989D\u5EA6\u6821\u9A8C\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002");
31270
+ }
31271
+ }
31272
+ async function readQuotaResponse(response) {
31273
+ try {
31274
+ const value = await response.json();
31275
+ if (value && typeof value === "object" && !Array.isArray(value)) {
31276
+ return value;
31277
+ }
31278
+ } catch {
31279
+ }
31280
+ return {};
31281
+ }
31282
+ function formatQuotaExceededMessage(serverName, body) {
31283
+ if (body.message) return body.message;
31284
+ const label = serverName === "seedance" ? "\u751F\u89C6\u9891" : serverName === "seedream" ? "\u751F\u56FE" : "\u5B98\u65B9\u5A92\u4F53";
31285
+ if (typeof body.used === "number" && typeof body.limit === "number") {
31286
+ return `\u4ECA\u5929\u7684${label}\u989D\u5EA6\u5DF2\u7528\u5B8C\uFF08${body.used}/${body.limit}\uFF09\u3002\u8BF7\u660E\u5929\u518D\u8BD5\uFF0C\u6216\u8054\u7CFB\u8FD0\u8425\u8C03\u6574\u989D\u5EA6\u3002`;
31287
+ }
31288
+ return `\u4ECA\u5929\u7684${label}\u989D\u5EA6\u5DF2\u7528\u5B8C\u3002\u8BF7\u660E\u5929\u518D\u8BD5\uFF0C\u6216\u8054\u7CFB\u8FD0\u8425\u8C03\u6574\u989D\u5EA6\u3002`;
31289
+ }
31290
+
31234
31291
  // src/seedanceMcpCli.ts
31235
31292
  var SERVER_NAME = "ahchat-seedance-mcp";
31236
31293
  var SERVER_VERSION = "0.1.0";
@@ -31292,7 +31349,7 @@ function createServer() {
31292
31349
  const server = new McpServer(
31293
31350
  { name: SERVER_NAME, version: SERVER_VERSION },
31294
31351
  {
31295
- instructions: "AHChat official Seedance video generation MCP. Call seedance_create_task to submit a job, then seedance_check_task to poll the task_id. Generated URLs can expire; show or download them promptly."
31352
+ instructions: "AHChat official Seedance video generation MCP. Each user request should create exactly one video task. Call seedance_create_task once to submit a job, then seedance_check_task only when progress is requested. Generated URLs can expire; show or download them promptly."
31296
31353
  }
31297
31354
  );
31298
31355
  server.registerTool(
@@ -31315,7 +31372,7 @@ function createServer() {
31315
31372
  "seedance_create_task",
31316
31373
  {
31317
31374
  title: "Create Seedance video generation task",
31318
- description: "Submit a Seedance video generation task to the Volcengine Ark API and return the task_id immediately. Poll seedance_check_task afterwards.",
31375
+ description: "Submit exactly one Seedance video generation task to the Volcengine Ark API and return the task_id immediately. In AHChat, do not submit alternate tasks, loop, or sleep afterwards; the media task card tracks status.",
31319
31376
  inputSchema: createTaskInputShape,
31320
31377
  annotations: {
31321
31378
  readOnlyHint: false,
@@ -31328,6 +31385,7 @@ function createServer() {
31328
31385
  if (validationError) return errorResult(validationError);
31329
31386
  try {
31330
31387
  const normalized = normalizeCreateTaskInput(input);
31388
+ await consumeOfficialMcpDailyQuota("seedance", "seedance_create_task");
31331
31389
  const result = await createSeedanceTask(normalized);
31332
31390
  const payload = {
31333
31391
  state: "submitted",
@@ -31363,12 +31421,16 @@ function createServer() {
31363
31421
  async (input) => {
31364
31422
  try {
31365
31423
  const parsed = checkTaskInputSchema.parse(input);
31366
- const result = await checkSeedanceTask(parsed.task_id);
31424
+ const result = await withDownloadedSeedanceVideo(await checkSeedanceTask(parsed.task_id));
31367
31425
  const payload = {
31368
31426
  task_id: result.id,
31369
31427
  status: result.status,
31370
31428
  video_url: result.videoUrl,
31371
31429
  last_frame_url: result.lastFrameUrl,
31430
+ local_path: result.localPath,
31431
+ file_name: result.fileName,
31432
+ mime_type: result.mimeType,
31433
+ download_error: result.downloadError,
31372
31434
  fail_reason: result.failReason
31373
31435
  };
31374
31436
  return {
@@ -31495,12 +31557,139 @@ async function checkSeedanceTask(taskId) {
31495
31557
  raw: obj
31496
31558
  };
31497
31559
  }
31560
+ async function withDownloadedSeedanceVideo(result) {
31561
+ if (!result.videoUrl) return result;
31562
+ try {
31563
+ const downloaded = await downloadGeneratedVideoToWorkdir(result.videoUrl, result.id);
31564
+ return {
31565
+ ...result,
31566
+ localPath: downloaded.localPath,
31567
+ fileName: downloaded.fileName,
31568
+ mimeType: downloaded.mimeType
31569
+ };
31570
+ } catch (error51) {
31571
+ return {
31572
+ ...result,
31573
+ downloadError: error51 instanceof Error ? error51.message : String(error51)
31574
+ };
31575
+ }
31576
+ }
31577
+ async function downloadGeneratedVideoToWorkdir(url2, taskId) {
31578
+ if (!/^https?:\/\//i.test(url2)) {
31579
+ throw new Error("generated video is not a downloadable URL");
31580
+ }
31581
+ const existingFileName = generatedVideoFileName(taskId, videoExtensionFromUrl(url2) ?? ".mp4");
31582
+ const existingPath = import_node_path.default.join(process.cwd(), existingFileName);
31583
+ if (await fileExists(existingPath)) {
31584
+ return {
31585
+ localPath: existingPath,
31586
+ fileName: existingFileName,
31587
+ mimeType: videoMimeTypeFromPath(existingPath) ?? "video/mp4"
31588
+ };
31589
+ }
31590
+ const response = await fetch(url2, { signal: AbortSignal.timeout(18e4) });
31591
+ if (!response.ok) {
31592
+ throw new Error(`download failed with HTTP ${response.status}`);
31593
+ }
31594
+ const buffer = Buffer.from(await response.arrayBuffer());
31595
+ const mimeType = videoMimeTypeFromContentType(response.headers.get("content-type")) ?? videoMimeTypeFromUrl(url2) ?? "video/mp4";
31596
+ const extension = videoExtensionForMimeType(mimeType);
31597
+ const fileName = generatedVideoFileName(taskId, extension);
31598
+ const filePath = import_node_path.default.join(process.cwd(), fileName);
31599
+ await import_promises.default.mkdir(process.cwd(), { recursive: true });
31600
+ try {
31601
+ await import_promises.default.writeFile(filePath, buffer, { flag: "wx" });
31602
+ } catch (error51) {
31603
+ if (!isFileExistsError(error51)) throw error51;
31604
+ }
31605
+ return { localPath: filePath, fileName, mimeType };
31606
+ }
31607
+ function generatedVideoFileName(taskId, extension) {
31608
+ const safeTaskId = taskId.replace(/[^A-Za-z0-9._-]+/g, "_").slice(0, 96) || "task";
31609
+ return `seedance-${safeTaskId}${extension}`;
31610
+ }
31611
+ async function fileExists(filePath) {
31612
+ try {
31613
+ await import_promises.default.access(filePath);
31614
+ return true;
31615
+ } catch {
31616
+ return false;
31617
+ }
31618
+ }
31619
+ function videoMimeTypeFromContentType(contentType) {
31620
+ if (!contentType) return null;
31621
+ return normalizeVideoMimeType(contentType.split(";")[0]?.trim() ?? "");
31622
+ }
31623
+ function videoMimeTypeFromUrl(url2) {
31624
+ try {
31625
+ return videoMimeTypeFromPath(new URL(url2).pathname);
31626
+ } catch {
31627
+ return videoMimeTypeFromPath(url2);
31628
+ }
31629
+ }
31630
+ function videoExtensionFromUrl(url2) {
31631
+ try {
31632
+ return videoExtensionFromPath(new URL(url2).pathname);
31633
+ } catch {
31634
+ return videoExtensionFromPath(url2);
31635
+ }
31636
+ }
31637
+ function videoMimeTypeFromPath(filePath) {
31638
+ switch (import_node_path.default.extname(filePath).toLowerCase()) {
31639
+ case ".mov":
31640
+ return "video/quicktime";
31641
+ case ".webm":
31642
+ return "video/webm";
31643
+ case ".m4v":
31644
+ return "video/x-m4v";
31645
+ case ".mp4":
31646
+ return "video/mp4";
31647
+ default:
31648
+ return null;
31649
+ }
31650
+ }
31651
+ function videoExtensionFromPath(filePath) {
31652
+ switch (import_node_path.default.extname(filePath).toLowerCase()) {
31653
+ case ".mov":
31654
+ case ".webm":
31655
+ case ".m4v":
31656
+ case ".mp4":
31657
+ return import_node_path.default.extname(filePath).toLowerCase();
31658
+ default:
31659
+ return null;
31660
+ }
31661
+ }
31662
+ function normalizeVideoMimeType(mimeType) {
31663
+ const normalized = mimeType.toLowerCase();
31664
+ if (normalized === "video/mp4" || normalized === "video/quicktime" || normalized === "video/webm" || normalized === "video/x-m4v") {
31665
+ return normalized;
31666
+ }
31667
+ return null;
31668
+ }
31669
+ function videoExtensionForMimeType(mimeType) {
31670
+ switch (mimeType) {
31671
+ case "video/quicktime":
31672
+ return ".mov";
31673
+ case "video/webm":
31674
+ return ".webm";
31675
+ case "video/x-m4v":
31676
+ return ".m4v";
31677
+ default:
31678
+ return ".mp4";
31679
+ }
31680
+ }
31681
+ function isFileExistsError(error51) {
31682
+ return Boolean(error51 && typeof error51 === "object" && error51.code === "EEXIST");
31683
+ }
31498
31684
  async function parseJsonSafe(response) {
31499
31685
  const text = await response.text();
31500
31686
  if (!text) return void 0;
31501
31687
  try {
31502
31688
  return JSON.parse(text);
31503
- } catch {
31689
+ } catch (error51) {
31690
+ writeStderr(
31691
+ `[${SERVER_NAME}] ARK API returned a non-JSON response body: ${error51 instanceof Error ? error51.message : String(error51)}`
31692
+ );
31504
31693
  return text;
31505
31694
  }
31506
31695
  }
@@ -31579,22 +31768,27 @@ function errorResult(message) {
31579
31768
  function usageGuide() {
31580
31769
  return [
31581
31770
  "AHChat Seedance workflow:",
31582
- "1. Call seedance_create_task with prompt and optional reference media URLs.",
31583
- "2. Wait 30-90 seconds, then call seedance_check_task with the returned task_id.",
31584
- "3. When status is succeeded, use video_url in the chat result. Signed URLs may expire.",
31771
+ "1. Call seedance_create_task with prompt and optional reference media URLs. Each user request creates exactly one video task.",
31772
+ "2. After submission, stop manual waiting. AHChat will track the task in the visible media card.",
31773
+ "3. Only call seedance_check_task once when the user explicitly asks for progress or when diagnostics are needed.",
31774
+ "4. When status is succeeded, keep the text reply short and let the media card show preview, download, copy, and regenerate actions. Signed URLs may expire.",
31585
31775
  "",
31586
31776
  "Common defaults: model=doubao-seedance-2-0-260128, duration=5, ratio=16:9, resolution=720p, generate_audio=true.",
31587
31777
  "For vertical short video, set ratio=9:16. Reference media URLs must be reachable by Volcengine Ark."
31588
31778
  ].join("\n");
31589
31779
  }
31780
+ function writeStderr(message) {
31781
+ process.stderr.write(`${message}
31782
+ `);
31783
+ }
31590
31784
  async function main() {
31591
31785
  const server = createServer();
31592
31786
  const transport = new StdioServerTransport();
31593
31787
  await server.connect(transport);
31594
- console.error(`${SERVER_NAME} v${SERVER_VERSION} running on stdio`);
31788
+ writeStderr(`${SERVER_NAME} v${SERVER_VERSION} running on stdio`);
31595
31789
  }
31596
31790
  main().catch((error51) => {
31597
31791
  const message = error51 instanceof Error ? error51.stack ?? error51.message : String(error51);
31598
- console.error(`Fatal error in ${SERVER_NAME}: ${message}`);
31792
+ writeStderr(`Fatal error in ${SERVER_NAME}: ${message}`);
31599
31793
  process.exit(1);
31600
31794
  });