/** * 修复 voc/* 和 ecom orchestrators 的 apigId * Vo3ROWEvDy (social) → 7HwdQZk55B (ecom) * * 方式:纯字符串替换,只改 tokenConfig 和 paymentParams 里那两处,避免破坏其他字段 */ const fs = require('fs'); const path = require('path'); const ROOT = path.resolve(__dirname, '..', '..'); // 需要改成 7HwdQZk55B 的文件(endpoint 或 pipeline 主要调用 voc-ecom API) const ECOM_SKILLS = [ // 12 direct-endpoint 'voc/asin-reverse-keywords', 'voc/asin-sales-volume', 'voc/category-products', 'voc/category-tree', 'voc/keyword-product-ranking', 'voc/keyword-search', 'voc/keyword-search-trend', 'voc/product-detail-query', 'voc/product-monitor', 'voc/product-reviews-query', 'voc/product-search', 'voc/similar-products', // 5 orchestrators (pipelines 调用 /api/ProductRequest /api/AsinSalesVolume 等 ecom API) 'competitor-analysis/competitor-bsr-tracking', 'competitor-analysis/competitor-discovery', 'competitor-analysis/competitor-pricing-analysis', 'competitor-analysis/competitor-product-comparison', 'review-analysis/review-batch-collection', 'review-analysis/review-keyword-cloud', 'synthesis/brand-profile', 'synthesis/category-landscape', 'synthesis/product-deep-analysis' ]; const TARGET_APIG = '7HwdQZk55B'; const OLD_APIG = 'Vo3ROWEvDy'; let totalFiles = 0, totalReplacements = 0, errors = []; for (const skillDir of ECOM_SKILLS) { const fp = path.join(ROOT, skillDir, 'api-config.json'); if (!fs.existsSync(fp)) { errors.push(`NOT FOUND: ${skillDir}`); continue; } const raw = fs.readFileSync(fp, 'utf-8'); const cfg = JSON.parse(raw); let modified = false; let count = 0; // 1. tokenConfig.apigId if (cfg.tokenConfig?.apigId === OLD_APIG) { cfg.tokenConfig.apigId = TARGET_APIG; modified = true; count++; } // 2. tokenConfig.paymentUrlResolution.paymentParams.apigid if (cfg.tokenConfig?.paymentUrlResolution?.paymentParams?.apigid === OLD_APIG) { cfg.tokenConfig.paymentUrlResolution.paymentParams.apigid = TARGET_APIG; modified = true; count++; } // 3. 扫描 onMissing.qrCodeUrl (字符串包含 apigid=Vo3ROWEvDy) if (cfg.tokenConfig?.onMissing?.qrCodeUrl?.includes(`apigid=${OLD_APIG}`)) { cfg.tokenConfig.onMissing.qrCodeUrl = cfg.tokenConfig.onMissing.qrCodeUrl.replace( new RegExp(`apigid=${OLD_APIG}`, 'g'), `apigid=${TARGET_APIG}` ); modified = true; count++; } // 4. steps / description 里的 URL 字符串 const scanString = (obj, parent, key) => { if (typeof obj === 'string' && obj.includes(OLD_APIG) && obj.includes('apigid')) { parent[key] = obj.replace(new RegExp(`apigid=${OLD_APIG}`, 'g'), `apigid=${TARGET_APIG}`); count++; modified = true; } else if (typeof obj === 'object' && obj !== null) { for (const [k, v] of Object.entries(obj)) scanString(v, obj, k); } }; // 仅扫 paymentUrlResolution.steps 之类的地方 if (cfg.tokenConfig?.paymentUrlResolution?.steps) { cfg.tokenConfig.paymentUrlResolution.steps = cfg.tokenConfig.paymentUrlResolution.steps.map(s => typeof s === 'string' ? s.replace(new RegExp(`apigid=${OLD_APIG}`, 'g'), `apigid=${TARGET_APIG}`) : s ); } if (modified) { fs.writeFileSync(fp, JSON.stringify(cfg, null, 2) + '\n', 'utf-8'); totalFiles++; totalReplacements += count; console.log(` [${count}] ${skillDir}/api-config.json`); } } console.log(''); console.log(`Done: ${totalFiles} files modified, ${totalReplacements} replacements total`); if (errors.length) { console.log('\nErrors:'); errors.forEach(e => console.log(' ', e)); }