build-platform-index.js 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/env node
  2. 'use strict';
  3. // 从接口目录生成 skills/voc-api-catalog/references/platforms.md。
  4. // 这份索引是给 Agent 离线速查用的:有哪些平台、各有多少接口、常用接口 id 是什么。
  5. // 目录变更后运行 `npm run catalog:index` 重新生成,不要手改生成的文件。
  6. const fs = require('fs');
  7. const path = require('path');
  8. const { catalogOverview, listEndpoints, hydrateEndpoint } = require('../mcp/src/core/api-catalog');
  9. const OUTPUT = path.resolve(__dirname, '..', 'skills', 'voc-api-catalog', 'references', 'platforms.md');
  10. const PER_PLATFORM = 6;
  11. const COMMON_USE = /search|comment|detail|review|hot|user_post|posted|list|搜索|评论|详情|热/i;
  12. function rank(endpoint) {
  13. let score = Number(endpoint.recommendationPriority || 0);
  14. if (endpoint.verifiedAt) score += 100;
  15. if (COMMON_USE.test(`${endpoint.id} ${endpoint.title}`)) score += 10;
  16. return score;
  17. }
  18. function pickEndpoints(channel, platform) {
  19. return listEndpoints()
  20. .filter(item => !item.deprecated && (item.channel || 'social') === channel && item.platform === platform)
  21. .map((item, index) => ({ item, index, score: rank(item) }))
  22. .sort((a, b) => (b.score - a.score) || (a.index - b.index))
  23. .slice(0, PER_PLATFORM)
  24. .map(entry => hydrateEndpoint(entry.item));
  25. }
  26. function build() {
  27. const overview = catalogOverview();
  28. const lines = [
  29. '# 平台与接口索引',
  30. '',
  31. `> 由接口目录自动生成(目录 v${overview.version},${overview.updatedAt})。不要手改,目录变更后重新生成。`,
  32. '',
  33. `共 ${overview.active} 个可用接口。本文件每个平台只列最常用的几个,完整清单用 \`voc_api_search\` 传 \`platform\` 查。`,
  34. '',
  35. '用法:在下面找到平台 key 和接口 id → `voc_api_doc` 传 id 读参数 → `voc_api_call` 调用。',
  36. '带 ✓ 的接口在目录更新当天做过真实调用验证。没有 ✓ 不代表不可用,只是没有逐个实测。',
  37. ''
  38. ];
  39. for (const channel of overview.channels) {
  40. lines.push(`## ${channel.label}(channel=${channel.channel})`, '');
  41. lines.push(`${channel.platforms.length} 个平台,${channel.active} 个接口。`, '');
  42. for (const platform of channel.platforms) {
  43. lines.push(`### ${platform.label} \`${platform.key}\`(${platform.active} 个)`, '');
  44. for (const endpoint of pickEndpoints(channel.channel, platform.key)) {
  45. const required = (endpoint.params || []).filter(param => param.required).map(param => param.name);
  46. const mark = endpoint.verifiedAt ? ' ✓' : '';
  47. lines.push(`- \`${endpoint.id}\`${mark} ${endpoint.title}。必填:${required.join(', ') || '无'}`);
  48. }
  49. lines.push('');
  50. }
  51. }
  52. return `${lines.join('\n').replace(/\n+$/, '')}\n`;
  53. }
  54. if (require.main === module) {
  55. const content = build();
  56. if (process.argv.includes('--check')) {
  57. const current = fs.existsSync(OUTPUT) ? fs.readFileSync(OUTPUT, 'utf8') : '';
  58. if (current !== content) {
  59. console.error('platforms.md 与接口目录不一致,请运行 npm run catalog:index');
  60. process.exit(1);
  61. }
  62. console.log('platforms.md is up to date');
  63. } else {
  64. fs.writeFileSync(OUTPUT, content, 'utf8');
  65. console.log(`wrote ${path.relative(process.cwd(), OUTPUT)} (${content.split('\n').length} lines)`);
  66. }
  67. }
  68. module.exports = { build, OUTPUT };