deploy-to-openclaw.ps1 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. # OpenClaw VOC Skills Deploy Script v3.0.0
  2. #
  3. # Deploys skills, workshop playbook, and memory templates to OpenClaw
  4. #
  5. # Usage:
  6. # .\deploy-to-openclaw.ps1 # Deploy everything
  7. # .\deploy-to-openclaw.ps1 -DryRun # Preview only
  8. # .\deploy-to-openclaw.ps1 -SkillsOnly # Deploy skills only
  9. # .\deploy-to-openclaw.ps1 -WorkshopOnly # Deploy workshop only
  10. # .\deploy-to-openclaw.ps1 -OpenClawDir "D:\path" # Custom OpenClaw directory
  11. param(
  12. [string]$OpenClawDir = "$env:USERPROFILE\.openclaw",
  13. [string]$ProjectRoot = (Split-Path (Split-Path $PSScriptRoot -Parent) -Parent),
  14. [switch]$SkillsOnly,
  15. [switch]$WorkshopOnly,
  16. [switch]$DryRun
  17. )
  18. $ErrorActionPreference = "Stop"
  19. $utf8NoBom = New-Object System.Text.UTF8Encoding($false)
  20. # ============================================
  21. # Paths
  22. # ============================================
  23. $SkillsTarget = Join-Path $OpenClawDir "skills"
  24. $WorkspaceTarget = Join-Path $OpenClawDir "workspace"
  25. $MemoryTarget = Join-Path $WorkspaceTarget "memory"
  26. $ToolsTarget = Join-Path $OpenClawDir "tools"
  27. $WorkspaceToolsTarget = Join-Path $WorkspaceTarget "scripts\tools"
  28. $ToolItems = @(
  29. @{ Source = Join-Path $ProjectRoot "scripts\tools\openclaw-tool-runner.js"; Dest = "openclaw-tool-runner.js" },
  30. @{ Source = Join-Path $ProjectRoot "scripts\tools\set-voc-token.js"; Dest = "set-voc-token.js" },
  31. @{ Source = Join-Path $ProjectRoot "scripts\tools\voc-token-preflight.js"; Dest = "voc-token-preflight.js" },
  32. @{ Source = Join-Path $ProjectRoot "scripts\tools\voc-single-platform-report.js"; Dest = "voc-single-platform-report.js" },
  33. @{ Source = Join-Path $ProjectRoot "scripts\tools\voc-data-normalizer.js"; Dest = "voc-data-normalizer.js" },
  34. @{ Source = Join-Path $ProjectRoot "scripts\tools\platform-mini-report-generator.js"; Dest = "platform-mini-report-generator.js" },
  35. @{ Source = Join-Path $ProjectRoot "scripts\tools\chapter-insight-writer.js"; Dest = "chapter-insight-writer.js" },
  36. @{ Source = Join-Path $ProjectRoot "scripts\tools\voc-report-auditor.js"; Dest = "voc-report-auditor.js" },
  37. @{ Source = Join-Path $ProjectRoot "voc-report-factory\html-v2\gen-report-template.js"; Dest = "voc-report-factory\html-v2\gen-report-template.js" },
  38. @{ Source = Join-Path $ProjectRoot "voc-report-factory\html-v2\report-renderer-template.js"; Dest = "voc-report-factory\html-v2\report-renderer-template.js" },
  39. @{ Source = Join-Path $ProjectRoot "demo\xiaohongshu\raw-notes.json"; Dest = "course-samples\xiaohongshu\raw-notes.json" },
  40. @{ Source = Join-Path $ProjectRoot "demo\xiaohongshu\raw-comments.json"; Dest = "course-samples\xiaohongshu\raw-comments.json" }
  41. )
  42. $SkillCategories = @(
  43. "voc",
  44. "social-media",
  45. "competitor-analysis",
  46. "review-analysis",
  47. "synthesis",
  48. "social-voc",
  49. "douyin",
  50. "xiaohongshu",
  51. "video-creation",
  52. "jimeng",
  53. "payment",
  54. "test"
  55. )
  56. # ============================================
  57. # Helper Functions
  58. # ============================================
  59. function Remove-Bom {
  60. param([string]$FilePath)
  61. $bytes = [System.IO.File]::ReadAllBytes($FilePath)
  62. if ($bytes.Length -ge 3 -and $bytes[0] -eq 0xEF -and $bytes[1] -eq 0xBB -and $bytes[2] -eq 0xBF) {
  63. $clean = $bytes[3..($bytes.Length - 1)]
  64. [System.IO.File]::WriteAllBytes($FilePath, $clean)
  65. return $true
  66. }
  67. return $false
  68. }
  69. function Test-SkillValid {
  70. param([string]$SkillDir, [string]$SkillName)
  71. $skillMd = Join-Path $SkillDir "SKILL.md"
  72. $apiConf = Join-Path $SkillDir "api-config.json"
  73. if (-not (Test-Path $skillMd)) {
  74. Write-Host " [SKIP] $SkillName - missing SKILL.md" -ForegroundColor DarkGray
  75. return $false
  76. }
  77. # Validate YAML frontmatter
  78. $mdContent = [System.IO.File]::ReadAllText($skillMd, [System.Text.Encoding]::UTF8)
  79. if ($mdContent -notmatch "^---\s*\r?\n") {
  80. Write-Host " [WARN] $SkillName - SKILL.md missing YAML frontmatter" -ForegroundColor Yellow
  81. }
  82. # Validate api-config.json if exists
  83. if (Test-Path $apiConf) {
  84. try {
  85. $null = Get-Content $apiConf -Raw -Encoding UTF8 | ConvertFrom-Json
  86. } catch {
  87. Write-Host " [ERROR] $SkillName - invalid JSON in api-config.json" -ForegroundColor Red
  88. return $false
  89. }
  90. }
  91. return $true
  92. }
  93. function Deploy-File {
  94. param([string]$Source, [string]$Dest, [string]$Label)
  95. if ($DryRun) {
  96. $tag = if (Test-Path $Dest) { "UPDATE" } else { "NEW" }
  97. Write-Host " [$tag] $Label" -ForegroundColor White
  98. } else {
  99. $destDir = Split-Path $Dest -Parent
  100. if (-not (Test-Path $destDir)) {
  101. New-Item -ItemType Directory -Path $destDir -Force | Out-Null
  102. }
  103. Copy-Item $Source -Destination $Dest -Force
  104. }
  105. }
  106. # ============================================
  107. # Banner
  108. # ============================================
  109. Write-Host ""
  110. Write-Host " ====================================================" -ForegroundColor Cyan
  111. Write-Host " OpenClaw VOC Skills Deploy v3.0.0" -ForegroundColor Cyan
  112. Write-Host " ====================================================" -ForegroundColor Cyan
  113. Write-Host ""
  114. Write-Host " Project: $ProjectRoot"
  115. Write-Host " OpenClaw: $OpenClawDir"
  116. if ($DryRun) { Write-Host " Mode: DRY RUN (preview only)" -ForegroundColor Yellow }
  117. if ($SkillsOnly) { Write-Host " Scope: Skills only" -ForegroundColor Yellow }
  118. if ($WorkshopOnly) { Write-Host " Scope: Workshop only" -ForegroundColor Yellow }
  119. Write-Host ""
  120. # Pre-flight check
  121. if (-not (Test-Path $OpenClawDir)) {
  122. Write-Host " [ERROR] OpenClaw directory not found: $OpenClawDir" -ForegroundColor Red
  123. Write-Host " Is OpenClaw installed? Specify -OpenClawDir to override." -ForegroundColor Yellow
  124. exit 1
  125. }
  126. $totalDeployed = 0
  127. $totalSkipped = 0
  128. $totalErrors = 0
  129. $bomFixed = 0
  130. # ============================================
  131. # Part 1: Deploy Skills
  132. # ============================================
  133. if (-not $WorkshopOnly) {
  134. Write-Host " [1/3] Deploying Skills" -ForegroundColor Cyan
  135. Write-Host " $('-' * 48)"
  136. if (-not $DryRun -and -not (Test-Path $SkillsTarget)) {
  137. New-Item -ItemType Directory -Path $SkillsTarget -Force | Out-Null
  138. }
  139. foreach ($cat in $SkillCategories) {
  140. $catPath = Join-Path $ProjectRoot $cat
  141. if (-not (Test-Path $catPath)) { continue }
  142. $skillDirs = Get-ChildItem -Directory -Path $catPath -ErrorAction SilentlyContinue
  143. if ($null -eq $skillDirs -or $skillDirs.Count -eq 0) { continue }
  144. Write-Host " [$cat]" -ForegroundColor DarkCyan
  145. foreach ($skillDir in $skillDirs) {
  146. $skillName = $skillDir.Name
  147. if (-not (Test-SkillValid $skillDir.FullName $skillName)) {
  148. $totalSkipped++
  149. continue
  150. }
  151. $destDir = Join-Path $SkillsTarget $skillName
  152. if ($DryRun) {
  153. $tag = if (Test-Path $destDir) { "UPDATE" } else { "NEW" }
  154. Write-Host " [$tag] $skillName" -ForegroundColor White
  155. } else {
  156. if (Test-Path $destDir) {
  157. Remove-Item -Recurse -Force $destDir
  158. }
  159. New-Item -ItemType Directory -Path $destDir -Force | Out-Null
  160. Get-ChildItem -File -Path $skillDir.FullName | ForEach-Object {
  161. Copy-Item $_.FullName -Destination $destDir
  162. }
  163. # Fix UTF-8 BOM in SKILL.md (causes OpenClaw parsing failures)
  164. $deployedMd = Join-Path $destDir "SKILL.md"
  165. if (Test-Path $deployedMd) {
  166. if (Remove-Bom $deployedMd) {
  167. $bomFixed++
  168. }
  169. }
  170. Write-Host " [OK] $skillName" -ForegroundColor Green
  171. }
  172. $totalDeployed++
  173. }
  174. }
  175. # Also deploy workshop skills (e.g. brand-context-builder)
  176. $workshopSkillsPath = Join-Path $ProjectRoot "workshop"
  177. if (Test-Path $workshopSkillsPath) {
  178. $wsSkills = Get-ChildItem -Directory -Path $workshopSkillsPath | Where-Object {
  179. Test-Path (Join-Path $_.FullName "SKILL.md")
  180. }
  181. if ($wsSkills.Count -gt 0) {
  182. Write-Host " [workshop skills]" -ForegroundColor DarkCyan
  183. foreach ($ws in $wsSkills) {
  184. $skillName = $ws.Name
  185. $destDir = Join-Path $SkillsTarget $skillName
  186. if ($DryRun) {
  187. $tag = if (Test-Path $destDir) { "UPDATE" } else { "NEW" }
  188. Write-Host " [$tag] $skillName" -ForegroundColor White
  189. } else {
  190. if (Test-Path $destDir) { Remove-Item -Recurse -Force $destDir }
  191. New-Item -ItemType Directory -Path $destDir -Force | Out-Null
  192. Get-ChildItem -File -Path $ws.FullName | ForEach-Object {
  193. Copy-Item $_.FullName -Destination $destDir
  194. }
  195. $deployedMd = Join-Path $destDir "SKILL.md"
  196. if (Test-Path $deployedMd) { if (Remove-Bom $deployedMd) { $bomFixed++ } }
  197. Write-Host " [OK] $skillName" -ForegroundColor Green
  198. }
  199. $totalDeployed++
  200. }
  201. }
  202. }
  203. Write-Host ""
  204. }
  205. # ============================================
  206. # Part 2: Deploy Workshop Playbooks (v3 split-file architecture)
  207. # ============================================
  208. if (-not $SkillsOnly) {
  209. Write-Host " [2/3] Deploying Workshop Playbooks" -ForegroundColor Cyan
  210. Write-Host " $('-' * 48)"
  211. # v3: Deploy all workshop .md files (global rules + session files + supporting docs)
  212. $workshopMdFiles = Get-ChildItem -File -Path (Join-Path $ProjectRoot "workshop") -Filter "*.md" -ErrorAction SilentlyContinue
  213. if ($workshopMdFiles -and $workshopMdFiles.Count -gt 0) {
  214. foreach ($mdFile in $workshopMdFiles) {
  215. $dest = Join-Path $WorkspaceTarget $mdFile.Name
  216. Deploy-File $mdFile.FullName $dest $mdFile.Name
  217. if (-not $DryRun) { Write-Host " [OK] $($mdFile.Name)" -ForegroundColor Green }
  218. $totalDeployed++
  219. }
  220. } else {
  221. Write-Host " [SKIP] No workshop .md files found" -ForegroundColor DarkGray
  222. }
  223. Write-Host ""
  224. }
  225. # ============================================
  226. # Part 3: Deploy Memory Templates
  227. # ============================================
  228. if (-not $SkillsOnly) {
  229. Write-Host " [3/3] Deploying Memory Templates" -ForegroundColor Cyan
  230. Write-Host " $('-' * 48)"
  231. $memSrc = Join-Path $ProjectRoot "workshop\memory-templates"
  232. if (Test-Path $memSrc) {
  233. if (-not $DryRun -and -not (Test-Path $MemoryTarget)) {
  234. New-Item -ItemType Directory -Path $MemoryTarget -Force | Out-Null
  235. }
  236. Get-ChildItem -File -Path $memSrc -Filter "*.json" | ForEach-Object {
  237. $dest = Join-Path $MemoryTarget $_.Name
  238. Deploy-File $_.FullName $dest $_.Name
  239. if (-not $DryRun) { Write-Host " [OK] $($_.Name)" -ForegroundColor Green }
  240. $totalDeployed++
  241. }
  242. } else {
  243. Write-Host " [SKIP] memory-templates directory not found" -ForegroundColor DarkGray
  244. }
  245. Write-Host ""
  246. }
  247. # ============================================
  248. # Part 4: Deploy Local Tools
  249. # ============================================
  250. Write-Host " [4/4] Deploying Local Tools" -ForegroundColor Cyan
  251. Write-Host " $('-' * 48)"
  252. foreach ($tool in $ToolItems) {
  253. if (-not (Test-Path $tool.Source)) {
  254. Write-Host " [SKIP] $($tool.Dest) - source not found" -ForegroundColor DarkGray
  255. $totalSkipped++
  256. continue
  257. }
  258. $toolsDest = Join-Path $ToolsTarget $tool.Dest
  259. $workspaceDest = Join-Path $WorkspaceToolsTarget $tool.Dest
  260. Deploy-File $tool.Source $toolsDest $tool.Dest
  261. Deploy-File $tool.Source $workspaceDest ("workspace/scripts/tools/" + $tool.Dest)
  262. if (-not $DryRun) { Write-Host " [OK] $($tool.Dest)" -ForegroundColor Green }
  263. $totalDeployed++
  264. }
  265. Write-Host ""
  266. # ============================================
  267. # Part 5: Credentials Template
  268. # ============================================
  269. if (-not $DryRun -and -not $WorkshopOnly) {
  270. $credTemplate = Join-Path $ProjectRoot "__config\voc-credentials.template.json"
  271. $credTarget = Join-Path $OpenClawDir "voc-credentials.template.json"
  272. $credFile = Join-Path $OpenClawDir "voc-credentials.json"
  273. if (Test-Path $credTemplate) {
  274. Copy-Item $credTemplate -Destination $credTarget -Force
  275. if (-not (Test-Path $credFile)) {
  276. Write-Host " [INFO] Credentials template copied. Fill in vocToken in:" -ForegroundColor Yellow
  277. Write-Host " $credFile" -ForegroundColor Yellow
  278. }
  279. }
  280. }
  281. # ============================================
  282. # Summary
  283. # ============================================
  284. Write-Host " ====================================================" -ForegroundColor Cyan
  285. Write-Host " Deploy Summary" -ForegroundColor Cyan
  286. Write-Host " ====================================================" -ForegroundColor Cyan
  287. Write-Host ""
  288. Write-Host " Deployed: $totalDeployed"
  289. Write-Host " Skipped: $totalSkipped"
  290. Write-Host " Errors: $totalErrors"
  291. if ($bomFixed -gt 0) {
  292. Write-Host " BOM fixed: $bomFixed" -ForegroundColor Yellow
  293. }
  294. Write-Host ""
  295. if ($DryRun) {
  296. Write-Host " Remove -DryRun to execute actual deployment." -ForegroundColor Yellow
  297. } else {
  298. Write-Host " Done! Restart OpenClaw gateway to load new skills." -ForegroundColor Green
  299. Write-Host ""
  300. Write-Host " Verify:" -ForegroundColor DarkGray
  301. Write-Host " openclaw skills list" -ForegroundColor DarkGray
  302. Write-Host " openclaw skills info <skill-name>" -ForegroundColor DarkGray
  303. Write-Host " Test-Path ~/.openclaw/workspace/scripts/tools/openclaw-tool-runner.js" -ForegroundColor DarkGray
  304. }
  305. Write-Host ""