#!/usr/bin/env node /** * 生成 AI Agent 友好元数据文件 * - robots.txt(含 AI 爬虫规则 + Content-Signal) * - sitemap.xml * - .well-known/api-catalog * - .well-known/openid-configuration * - .well-known/oauth-protected-resource * - .well-known/mcp/server-card.json * - .well-known/agent-skills/index.json */ const fs = require('fs'); const path = require('path'); const BuildLogger = require('./logger.js'); const projectRoot = path.join(__dirname, '..'); const distDir = path.join(projectRoot, 'dist'); const SITE_URL = 'https://esim.cosr.eu.org'; // 规范 URL 列表 const CANONICAL_URLS = [ { loc: '/', priority: '1.0', changefreq: 'daily' }, { loc: '/giffgaff', priority: '0.9', changefreq: 'weekly' }, { loc: '/simyo', priority: '0.9', changefreq: 'weekly' }, ]; /** * 生成 robots.txt * 包含:标准爬虫规则、AI 爬虫规则、Content-Signal 声明 */ function generateRobotsTxt() { return `# eSIM-Tools robots.txt # https://www.rfc-editor.org/rfc/rfc9309 User-agent: * Allow: / Disallow: /api/ Disallow: /bff/ Disallow: /src/ Disallow: /.netlify/ Disallow: /coverage/ # AI 爬虫规则 User-agent: GPTBot Allow: / Disallow: /api/ Disallow: /bff/ User-agent: OAI-SearchBot Allow: / Disallow: /api/ Disallow: /bff/ User-agent: Claude-Web Allow: / Disallow: /api/ Disallow: /bff/ User-agent: ClaudeBot Allow: / Disallow: /api/ Disallow: /bff/ User-agent: Google-Extended Allow: / Disallow: /api/ Disallow: /bff/ User-agent: Bingbot Allow: / Disallow: /api/ Disallow: /bff/ # 内容信号声明 (Content-Signal) # https://contentsignals.org/ Content-Signal: ai-train=no, search=yes, ai-input=no # 站点地图 Sitemap: ${SITE_URL}/sitemap.xml `; } /** * 生成 sitemap.xml */ function generateSitemap() { const urls = CANONICAL_URLS.map(({ loc, priority, changefreq }) => ` ${SITE_URL}${loc} ${changefreq} ${priority} `).join(''); return ` ${urls} `; } /** * 生成 API Catalog (RFC 9727) */ function generateApiCatalog() { return JSON.stringify({ linkset: [ { anchor: `${SITE_URL}/bff/`, 'service-desc': { href: `${SITE_URL}/docs/api`, title: 'eSIM Tools API Documentation' }, 'service-doc': { href: `${SITE_URL}/README.md`, title: 'Project README' }, status: { href: '/.netlify/functions/health', title: 'Health Check' } } ] }, null, 2); } /** * 生成 OAuth Protected Resource Metadata (RFC 9728) */ function generateOAuthProtectedResource() { return JSON.stringify({ resource: `${SITE_URL}`, authorization_servers: [ 'https://id.giffgaff.com' ], scopes_supported: [ 'openid', 'profile' ], bearer_methods_supported: [ 'header' ], resource_documentation: `${SITE_URL}/docs/api` }, null, 2); } /** * 生成 MCP Server Card (SEP-2127) */ function generateMcpServerCard() { return JSON.stringify({ $schema: 'https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json', name: 'esim-tools/server', version: '2.0.0', title: 'eSIM Tools MCP Server', description: 'eSIM management tools for Giffgaff and Simyo users - OAuth authentication, MFA verification, and eSIM activation', websiteUrl: SITE_URL, repository: 'https://github.com/Silentely/eSIM-Tools', remotes: [ { type: 'http', url: `${SITE_URL}/bff/` } ], capabilities: { tools: [ { name: 'giffgaff-oauth', description: 'Giffgaff OAuth token exchange and authentication' }, { name: 'simyo-esim-activate', description: 'Simyo eSIM activation and management' } ] } }, null, 2); } /** * 生成 OAuth Authorization Server Metadata (RFC 8414) * Giffgaff 使用外部 OpenID Connect 提供商 */ function generateOAuthDiscovery() { return JSON.stringify({ issuer: 'https://id.giffgaff.com', authorization_endpoint: 'https://id.giffgaff.com/authorize', token_endpoint: 'https://id.giffgaff.com/token', jwks_uri: 'https://id.giffgaff.com/.well-known/jwks.json', revocation_endpoint: 'https://id.giffgaff.com/revoke', scopes_supported: [ 'openid', 'profile', 'email' ], response_types_supported: [ 'code' ], grant_types_supported: [ 'authorization_code', 'refresh_token' ], token_endpoint_auth_methods_supported: [ 'client_secret_basic', 'client_secret_post' ], code_challenge_methods_supported: [ 'S256' ], service_documentation: `${SITE_URL}/docs/api` }, null, 2); } /** * 生成 Agent Skills Discovery Index */ function generateAgentSkillsIndex() { return JSON.stringify({ $schema: 'https://agentskills.io/schema/v0.2.0', skills: [ { name: 'giffgaff-esim', type: 'action', description: 'Manage Giffgaff eSIM: OAuth authentication, MFA verification, and eSIM activation via GraphQL', url: `${SITE_URL}/.well-known/agent-skills/giffgaff-esim`, sha256: 'placeholder' }, { name: 'simyo-esim', type: 'action', description: 'Manage Simyo eSIM: login, device swap, and eSIM activation', url: `${SITE_URL}/.well-known/agent-skills/simyo-esim`, sha256: 'placeholder' } ] }, null, 2); } /** * 确保目录存在 */ async function ensureDir(dir) { await fs.promises.mkdir(dir, { recursive: true }); } /** * 写入文件(带日志) */ async function writeFile(filePath, content) { const relativePath = path.relative(distDir, filePath); await ensureDir(path.dirname(filePath)); await fs.promises.writeFile(filePath, content, 'utf-8'); BuildLogger.log(` ✓ ${relativePath}`); } /** * 主函数 */ async function main() { BuildLogger.log('🤖 生成 AI Agent 元数据文件...'); // robots.txt await writeFile(path.join(distDir, 'robots.txt'), generateRobotsTxt()); // sitemap.xml await writeFile(path.join(distDir, 'sitemap.xml'), generateSitemap()); // .well-known/api-catalog await writeFile( path.join(distDir, '.well-known', 'api-catalog'), generateApiCatalog() ); // .well-known/oauth-protected-resource await writeFile( path.join(distDir, '.well-known', 'oauth-protected-resource'), generateOAuthProtectedResource() ); // .well-known/openid-configuration (OAuth Discovery) await writeFile( path.join(distDir, '.well-known', 'openid-configuration'), generateOAuthDiscovery() ); // .well-known/mcp/server-card.json await writeFile( path.join(distDir, '.well-known', 'mcp', 'server-card.json'), generateMcpServerCard() ); // .well-known/agent-skills/index.json await writeFile( path.join(distDir, '.well-known', 'agent-skills', 'index.json'), generateAgentSkillsIndex() ); BuildLogger.success(' AI Agent 元数据文件生成完成'); } main().catch(err => { console.error('生成 Agent 元数据失败:', err); process.exitCode = 1; });