# add-token-config.ps1 # 批量给所有 api-config.json 添加 tokenConfig + errorHandling # 并将 endpoint-based skill 的 Authorization 改为 {{vocToken}} 动态引用 param( [string]$SkillsRoot = (Split-Path $PSScriptRoot -Parent), [switch]$DryRun ) $utf8NoBom = New-Object System.Text.UTF8Encoding($false) $hardcodedToken = 'r:858b3ee92314d5447d1fc3cdc10462d7' # ============================================================ # tokenConfig + errorHandling 块(插入到最后一个 } 之前) # ============================================================ $injection = @' , "tokenConfig": { "type": "bearer", "configFile": "~/.openclaw/voc-credentials.json", "tokenField": "vocToken", "currentToken": "r:858b3ee92314d5447d1fc3cdc10462d7", "resolutionOrder": ["configFile", "currentToken"], "onMissing": { "action": "showPaymentPrompt", "paymentUrl": "", "title": "需要配置 VOC-AI Token", "message": "此 Skill 需要有效的 VOC-AI Token 才能调用数据接口。", "setupInstructions": "充值成功后,请将获得的 Token(格式: r:xxxxxxxx)告知 AI,AI 将自动创建或更新 ~/.openclaw/voc-credentials.json,写入 {\"vocToken\": \"您的Token值\"} 并保存,后续所有 VOC Skills 将自动使用此 Token。" }, "onBalanceInsufficient": { "action": "showPaymentPrompt", "paymentUrl": "", "title": "VOC-AI Token 余额不足", "message": "当前 Token 余额已不足以完成本次请求,请充值后告知 AI 更新 ~/.openclaw/voc-credentials.json 中的 vocToken 字段。" } }, "errorHandling": { "balanceInsufficient": { "conditions": [ { "responseField": "code", "operator": "in", "value": [-2, -3, -10, 402, 429] }, { "responseField": "msg", "operator": "contains", "value": ["余额不足", "insufficient", "balance", "quota"] }, { "responseField": "message", "operator": "contains", "value": ["余额不足", "insufficient", "balance"] } ], "matchMode": "any", "trigger": "tokenConfig.onBalanceInsufficient" }, "unauthorized": { "conditions": [ { "responseField": "code", "operator": "in", "value": [401, 403] }, { "responseField": "msg", "operator": "contains", "value": ["unauthorized", "token", "invalid", "auth"] } ], "matchMode": "any", "trigger": "tokenConfig.onMissing" } } '@ $categories = @("voc","social-media","competitor-analysis","review-analysis","synthesis","social-voc","douyin") $updated = 0 $skipped = 0 foreach ($cat in $categories) { $catPath = Join-Path $SkillsRoot $cat if (-not (Test-Path $catPath)) { continue } Get-ChildItem -Recurse -Filter "api-config.json" -Path $catPath | ForEach-Object { $filePath = $_.FullName $content = [System.IO.File]::ReadAllText($filePath, [System.Text.Encoding]::UTF8) # 跳过已处理的文件 if ($content -match '"tokenConfig"') { Write-Host " [SKIP] already patched: $($filePath.Replace($SkillsRoot+'\',''))" -ForegroundColor DarkGray $skipped++ return } # --- Step A: Authorization 改为 {{vocToken}}(仅 endpoint-based skill)--- $hasEndpoint = $content -match '"endpoint"' if ($hasEndpoint) { $content = $content.Replace( "Bearer $hardcodedToken", "Bearer {{vocToken}}" ) } # --- Step B: 插入 tokenConfig + errorHandling(最后一个 } 之前)--- $lastBrace = $content.LastIndexOf('}') if ($lastBrace -lt 0) { Write-Host " [ERROR] malformed JSON: $filePath" -ForegroundColor Red return } $newContent = $content.Substring(0, $lastBrace) + $injection + "`n}" if ($DryRun) { Write-Host " [DRY] $($filePath.Replace($SkillsRoot+'\',''))" -ForegroundColor Yellow } else { [System.IO.File]::WriteAllText($filePath, $newContent, $utf8NoBom) Write-Host " [OK] $($filePath.Replace($SkillsRoot+'\',''))" -ForegroundColor Green } $updated++ } } Write-Host "" Write-Host "Done: $updated updated, $skipped skipped" if ($DryRun) { Write-Host "Remove -DryRun to apply." -ForegroundColor Yellow }