mirror of
https://github.com/prompt-security/clawsec.git
synced 2026-06-23 10:21:21 +03:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0ba076ec53 | |||
| 2a76509fcf | |||
| 4a1cf246eb | |||
| 4c26671dc3 | |||
| de28dadd39 | |||
| f937384104 | |||
| 8648aad6d7 | |||
| 4a4b547b92 | |||
| 6f51e53cdd | |||
| d8dec965a8 | |||
| 9fd3059271 |
Generated
+406
@@ -0,0 +1,406 @@
|
||||
{
|
||||
"name": "clawhub-cli-pin",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "clawhub-cli-pin",
|
||||
"dependencies": {
|
||||
"clawhub": "0.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ark/schema": {
|
||||
"version": "0.56.0",
|
||||
"resolved": "https://registry.npmjs.org/@ark/schema/-/schema-0.56.0.tgz",
|
||||
"integrity": "sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==",
|
||||
"dependencies": {
|
||||
"@ark/util": "0.56.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ark/util": {
|
||||
"version": "0.56.0",
|
||||
"resolved": "https://registry.npmjs.org/@ark/util/-/util-0.56.0.tgz",
|
||||
"integrity": "sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA=="
|
||||
},
|
||||
"node_modules/@clack/core": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@clack/core/-/core-0.5.0.tgz",
|
||||
"integrity": "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow==",
|
||||
"dependencies": {
|
||||
"picocolors": "^1.0.0",
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@clack/prompts": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-0.11.0.tgz",
|
||||
"integrity": "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw==",
|
||||
"dependencies": {
|
||||
"@clack/core": "0.5.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/arkregex": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/arkregex/-/arkregex-0.0.5.tgz",
|
||||
"integrity": "sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==",
|
||||
"dependencies": {
|
||||
"@ark/util": "0.56.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arktype": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/arktype/-/arktype-2.2.0.tgz",
|
||||
"integrity": "sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==",
|
||||
"dependencies": {
|
||||
"@ark/schema": "0.56.0",
|
||||
"@ark/util": "0.56.0",
|
||||
"arkregex": "0.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/clawhub": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/clawhub/-/clawhub-0.7.0.tgz",
|
||||
"integrity": "sha512-volW6SbX8PawlnRxxCoUTKv5Pi+N3MrBi3hlO5/m9bVaO43UFciEeYti9+01c2U5n/SKhUkw7ASvnleyNmcoSA==",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^0.11.0",
|
||||
"arktype": "^2.1.29",
|
||||
"commander": "^14.0.2",
|
||||
"fflate": "^0.8.2",
|
||||
"ignore": "^7.0.5",
|
||||
"json5": "^2.2.3",
|
||||
"mime": "^4.1.0",
|
||||
"ora": "^9.0.0",
|
||||
"p-retry": "^7.1.1",
|
||||
"semver": "^7.7.3",
|
||||
"undici": "^7.16.0"
|
||||
},
|
||||
"bin": {
|
||||
"clawdhub": "bin/clawdhub.js",
|
||||
"clawhub": "bin/clawdhub.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-cursor": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/cli-spinners": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz",
|
||||
"integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==",
|
||||
"engines": {
|
||||
"node": ">=18.20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/commander": {
|
||||
"version": "14.0.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
|
||||
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz",
|
||||
"integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA=="
|
||||
},
|
||||
"node_modules/get-east-asian-width": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz",
|
||||
"integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "7.0.5",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
|
||||
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/is-interactive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
|
||||
"integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-network-error": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.2.tgz",
|
||||
"integrity": "sha512-PhBY86zaxNZUuWP6h13Vu5oFe0XY6/UlKzQnYFELzGVHygP3MxmvTfYSG7GN3aIab/iWudSMgjSnG9Dq+nHrgA==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/is-unicode-supported": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
|
||||
"integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/log-symbols": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz",
|
||||
"integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==",
|
||||
"dependencies": {
|
||||
"is-unicode-supported": "^2.0.0",
|
||||
"yoctocolors": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
|
||||
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa"
|
||||
],
|
||||
"bin": {
|
||||
"mime": "bin/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/mimic-function": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
||||
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/onetime": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
|
||||
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
|
||||
"dependencies": {
|
||||
"mimic-function": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/ora": {
|
||||
"version": "9.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ora/-/ora-9.4.0.tgz",
|
||||
"integrity": "sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==",
|
||||
"dependencies": {
|
||||
"chalk": "^5.6.2",
|
||||
"cli-cursor": "^5.0.0",
|
||||
"cli-spinners": "^3.2.0",
|
||||
"is-interactive": "^2.0.0",
|
||||
"is-unicode-supported": "^2.1.0",
|
||||
"log-symbols": "^7.0.1",
|
||||
"stdin-discarder": "^0.3.2",
|
||||
"string-width": "^8.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/p-retry": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz",
|
||||
"integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==",
|
||||
"dependencies": {
|
||||
"is-network-error": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
|
||||
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
|
||||
"dependencies": {
|
||||
"onetime": "^7.0.0",
|
||||
"signal-exit": "^4.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.8.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz",
|
||||
"integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/sisteransi": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
||||
},
|
||||
"node_modules/stdin-discarder": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.2.tgz",
|
||||
"integrity": "sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "8.2.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz",
|
||||
"integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==",
|
||||
"dependencies": {
|
||||
"get-east-asian-width": "^1.5.0",
|
||||
"strip-ansi": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
|
||||
"integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/undici": {
|
||||
"version": "7.28.0",
|
||||
"resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz",
|
||||
"integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==",
|
||||
"engines": {
|
||||
"node": ">=20.18.1"
|
||||
}
|
||||
},
|
||||
"node_modules/yoctocolors": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz",
|
||||
"integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "clawhub-cli-pin",
|
||||
"private": true,
|
||||
"description": "Pins the clawhub CLI used by skill-release.yml; package-lock.json provides the integrity hashes. Bump the version here and regenerate the lockfile with: npm install --package-lock-only",
|
||||
"dependencies": {
|
||||
"clawhub": "0.7.0"
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
@@ -53,9 +53,21 @@ jobs:
|
||||
|
||||
- name: Collect traffic
|
||||
env:
|
||||
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN || github.token }}
|
||||
# Traffic endpoints reject the Actions GITHUB_TOKEN ("Resource not
|
||||
# accessible by integration") — a PAT from a user with push access
|
||||
# is required: classic with repo scope, or fine-grained with read
|
||||
# access to Administration on this repository.
|
||||
GH_TRAFFIC_TOKEN: ${{ secrets.TRAFFIC_ARCHIVE_TOKEN }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
run: node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${GH_TRAFFIC_TOKEN}" ]; then
|
||||
echo "::error::No traffic-capable token configured. Set the TRAFFIC_ARCHIVE_TOKEN secret to a PAT with push access (classic: repo scope; fine-grained: Administration read)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
node scripts/archive-github-traffic.mjs --archive-dir "${TRAFFIC_ARCHIVE_DIR}"
|
||||
|
||||
- name: Commit archive
|
||||
run: |
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
name: Lint Python
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
name: Lint Shell Scripts
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: ShellCheck
|
||||
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0
|
||||
with:
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
name: Security Scan
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- name: Trivy FS Scan
|
||||
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # 0.34.1
|
||||
with:
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
name: Dependency Audit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
@@ -97,7 +97,7 @@ jobs:
|
||||
name: Advisory Feed Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
@@ -126,7 +126,7 @@ jobs:
|
||||
name: ClawSec Suite Verification Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
@@ -151,7 +151,7 @@ jobs:
|
||||
name: OpenClaw Audit Watchdog Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Verify signing key consistency (repo + docs)
|
||||
run: ./scripts/ci/verify_signing_key_consistency.sh
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: Translation Integrity Checks
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Verify signing key consistency (repo + docs)
|
||||
run: ./scripts/ci/verify_signing_key_consistency.sh
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
+235
-111
@@ -7,6 +7,8 @@ on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'skills/**'
|
||||
- '!skills/clawsec-feed/advisories/feed.json'
|
||||
- '!skills/clawsec-feed/advisories/feed.json.sig'
|
||||
- '.github/workflows/skill-release.yml'
|
||||
- 'scripts/ci/**'
|
||||
- 'scripts/test-skill-*.mjs'
|
||||
@@ -19,8 +21,8 @@ on:
|
||||
|
||||
permissions: read-all
|
||||
|
||||
env:
|
||||
CLAWHUB_CLI_VERSION: 0.7.0
|
||||
# The ClawHub CLI version is pinned (with integrity hashes) in
|
||||
# .github/clawhub-cli/package-lock.json — bump it there.
|
||||
|
||||
concurrency:
|
||||
group: skill-release-${{ github.ref }}
|
||||
@@ -34,7 +36,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -88,9 +90,19 @@ jobs:
|
||||
touched_skills_file="$(mktemp)"
|
||||
git diff --name-only "${BASE_SHA}...${HEAD_SHA}" -- \
|
||||
'skills/*/**' \
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json' \
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json.sig' \
|
||||
':(exclude)skills/*/test/**' \
|
||||
':(exclude)skills/*/tests/**' \
|
||||
| awk -F/ 'NF >= 3 {print $1 "/" $2}' \
|
||||
| awk -F/ '
|
||||
NF >= 3 {
|
||||
path = tolower($0)
|
||||
name = tolower($NF)
|
||||
if (path ~ /(^|\/)(__tests__|test|tests)\//) next
|
||||
if (name ~ /^(test|spec)[_-]/ || name ~ /\.(test|spec)\./) next
|
||||
print $1 "/" $2
|
||||
}
|
||||
' \
|
||||
| sort -u > "${touched_skills_file}"
|
||||
|
||||
if [ ! -s "${touched_skills_file}" ]; then
|
||||
@@ -257,7 +269,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -400,12 +412,17 @@ jobs:
|
||||
}
|
||||
|
||||
touched_skills_file="$(mktemp)"
|
||||
git diff --name-only "${BASE_SHA}...${HEAD_SHA}" -- 'skills/*/skill.json' 'skills/*/SKILL.md' \
|
||||
git diff --name-only "${BASE_SHA}...${HEAD_SHA}" -- \
|
||||
'skills/*/**' \
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json' \
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json.sig' \
|
||||
':(exclude)skills/*/test/**' \
|
||||
':(exclude)skills/*/tests/**' \
|
||||
| awk -F/ 'NF >= 3 {print $1 "/" $2}' \
|
||||
| sort -u > "${touched_skills_file}"
|
||||
|
||||
if [ ! -s "${touched_skills_file}" ]; then
|
||||
echo "No skill metadata files changed in this PR."
|
||||
echo "No release-relevant skill package files changed in this PR."
|
||||
rm -f "${touched_skills_file}"
|
||||
exit 0
|
||||
fi
|
||||
@@ -431,7 +448,8 @@ jobs:
|
||||
|
||||
is_test_release_path() {
|
||||
local lower="${1,,}"
|
||||
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]]
|
||||
local name="${lower##*/}"
|
||||
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == __tests__/* || "$lower" == */test/* || "$lower" == */tests/* || "$lower" == */__tests__/* || "$name" == test_* || "$name" == test-* || "$name" == spec_* || "$name" == spec-* || "$name" == *.test.* || "$name" == *.spec.* ]]
|
||||
}
|
||||
|
||||
generate_skillspector_report() {
|
||||
@@ -526,11 +544,6 @@ jobs:
|
||||
md_version_changed=true
|
||||
fi
|
||||
|
||||
if [ "${json_version_changed}" != "true" ] && [ "${md_version_changed}" != "true" ]; then
|
||||
echo "No version bump detected for ${skill_dir}; skipping dry-run."
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -z "${head_json_version}" ] || [ -z "${head_md_version}" ] || [ "${head_json_version}" != "${head_md_version}" ]; then
|
||||
echo "::error file=${skill_dir}::Version metadata is invalid for dry-run. Ensure validate-pr-version-sync passes."
|
||||
failures=$((failures + 1))
|
||||
@@ -619,9 +632,9 @@ jobs:
|
||||
# --- Create zip preserving directory structure ---
|
||||
zip_name="${skill_name}-v${version}.zip"
|
||||
(cd "${staging_dir}" && zip -qr "${OLDPWD}/${out_assets}/${zip_name}" .)
|
||||
if unzip -Z1 "${out_assets}/${zip_name}" | grep -Eiq '(^|/)(test|tests)/'; then
|
||||
if unzip -Z1 "${out_assets}/${zip_name}" | grep -Eiq '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.'; then
|
||||
echo "::error::Dry-run release archive contains test-only files: ${zip_name}"
|
||||
unzip -Z1 "${out_assets}/${zip_name}" | grep -Ei '(^|/)(test|tests)/' || true
|
||||
unzip -Z1 "${out_assets}/${zip_name}" | grep -Ei '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.' || true
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
|
||||
@@ -777,12 +790,177 @@ jobs:
|
||||
fi
|
||||
|
||||
if [ "${dry_run_count}" -eq 0 ]; then
|
||||
echo "No version bumps detected in changed skill metadata files."
|
||||
echo "No changed skill directories required dry-run assets."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Release dry-run completed successfully for ${dry_run_count} changed skill(s)."
|
||||
|
||||
- name: Prepare SkillSpector PR report artifact
|
||||
if: always()
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf dist/skillspector-pr-reports
|
||||
mkdir -p dist/dry-run dist/skillspector-pr-reports
|
||||
|
||||
found_reports=false
|
||||
while IFS= read -r report_path; do
|
||||
tag="${report_path#dist/dry-run/}"
|
||||
tag="${tag%%/*}"
|
||||
mkdir -p "dist/skillspector-pr-reports/${tag}"
|
||||
cp "${report_path}" "dist/skillspector-pr-reports/${tag}/skillspector-report.md"
|
||||
found_reports=true
|
||||
done < <(find dist/dry-run -path '*/release-assets/skillspector-report.md' -type f | sort)
|
||||
|
||||
if [ "${found_reports}" != "true" ]; then
|
||||
printf 'No SkillSpector reports were generated for this pull request.\n' > dist/skillspector-pr-reports/NO_SKILLSPECTOR_REPORTS.txt
|
||||
fi
|
||||
|
||||
- name: Upload SkillSpector PR reports
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: skillspector-pr-reports
|
||||
path: dist/skillspector-pr-reports
|
||||
retention-days: 14
|
||||
|
||||
comment-skillspector-report:
|
||||
if: always() && github.event_name == 'pull_request' && needs.release.result != 'cancelled'
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
issues: write
|
||||
steps:
|
||||
- name: Download SkillSpector reports
|
||||
continue-on-error: true
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: skillspector-pr-reports
|
||||
path: skillspector-pr-reports
|
||||
|
||||
- name: Comment SkillSpector reports
|
||||
continue-on-error: true
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
script: |
|
||||
const fs = require("node:fs/promises");
|
||||
const path = require("node:path");
|
||||
|
||||
const root = "skillspector-pr-reports";
|
||||
const maxCommentLength = 65000;
|
||||
|
||||
async function findReports(dir) {
|
||||
let entries;
|
||||
try {
|
||||
entries = await fs.readdir(dir, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") {
|
||||
return [];
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
const reports = [];
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
reports.push(...await findReports(fullPath));
|
||||
} else if (entry.isFile() && entry.name === "skillspector-report.md") {
|
||||
reports.push(fullPath);
|
||||
}
|
||||
}
|
||||
return reports;
|
||||
}
|
||||
|
||||
function tagFromReportPath(reportPath) {
|
||||
const parts = reportPath.split(path.sep);
|
||||
const releaseAssetsIndex = parts.lastIndexOf("release-assets");
|
||||
if (releaseAssetsIndex > 0) {
|
||||
return parts[releaseAssetsIndex - 1];
|
||||
}
|
||||
return path.basename(path.dirname(reportPath));
|
||||
}
|
||||
|
||||
function sanitizeReportForComment(report) {
|
||||
const omittedBlock = "_[code block omitted from PR comment; download the workflow artifact for raw details]_";
|
||||
return report
|
||||
.replace(/```[\s\S]*?```/g, omittedBlock)
|
||||
.split(/\r?\n/)
|
||||
.filter((line) => !/^\s{4,}\S/.test(line))
|
||||
.join("\n")
|
||||
.replace(/`[^`\n]*`/g, "`[inline snippet omitted]`")
|
||||
.replace(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi, "[redacted-email]")
|
||||
.replace(/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, "[redacted-aws-key]")
|
||||
.replace(/\b(?:ghp|gho|ghu|ghs|ghr|github_pat|glpat|xox[baprs]?|sk|pk)_[A-Za-z0-9_=-]{12,}\b/gi, "[redacted-token]")
|
||||
.replace(/\b[A-Za-z0-9+/]{40,}={0,2}\b/g, "[redacted-secret-like-value]")
|
||||
.trimEnd();
|
||||
}
|
||||
|
||||
function buildComment({ tag, report }) {
|
||||
const marker = `<!-- clawsec-skillspector-report:${tag} -->`;
|
||||
const sanitizedReport = sanitizeReportForComment(report);
|
||||
const footer = [
|
||||
"_Generated by the Skill Release dry-run for `" + tag + "`._",
|
||||
"_Raw snippets, code blocks, inline code, emails, and token-like values are omitted from this PR comment._",
|
||||
"_Download the `skillspector-pr-reports` workflow artifact for the full report._",
|
||||
].join("\n");
|
||||
let body = `${marker}\n${sanitizedReport}\n\n${footer}`;
|
||||
|
||||
if (body.length <= maxCommentLength) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const truncatedFooter = [
|
||||
"_Report truncated because it exceeds GitHub's comment size limit._",
|
||||
"_Download the `skillspector-pr-reports` workflow artifact for the full report._",
|
||||
footer,
|
||||
].join("\n");
|
||||
const budget = maxCommentLength - marker.length - truncatedFooter.length - 8;
|
||||
return `${marker}\n${sanitizedReport.slice(0, Math.max(0, budget)).trimEnd()}\n\n${truncatedFooter}`;
|
||||
}
|
||||
|
||||
const reports = await findReports(root);
|
||||
if (reports.length === 0) {
|
||||
core.info("No SkillSpector reports found; nothing to comment.");
|
||||
return;
|
||||
}
|
||||
|
||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
per_page: 100,
|
||||
});
|
||||
|
||||
for (const reportPath of reports.sort()) {
|
||||
const tag = tagFromReportPath(reportPath);
|
||||
const report = await fs.readFile(reportPath, "utf8");
|
||||
const marker = `<!-- clawsec-skillspector-report:${tag} -->`;
|
||||
const body = buildComment({ tag, report });
|
||||
const existing = comments.find((comment) => comment.body?.includes(marker));
|
||||
|
||||
if (existing) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: existing.id,
|
||||
body,
|
||||
});
|
||||
core.info(`Updated SkillSpector PR comment for ${tag}.`);
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body,
|
||||
});
|
||||
core.info(`Created SkillSpector PR comment for ${tag}.`);
|
||||
}
|
||||
}
|
||||
|
||||
simulate-tag-release-build:
|
||||
if: github.event_name == 'pull_request'
|
||||
needs: validate-pr-version-sync
|
||||
@@ -791,7 +969,7 @@ jobs:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -863,7 +1041,7 @@ jobs:
|
||||
echo "Parsed tag: skill=${SKILL_NAME}, version=${VERSION}"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Verify signing key consistency (repo + docs)
|
||||
run: ./scripts/ci/verify_signing_key_consistency.sh
|
||||
@@ -1065,7 +1243,8 @@ jobs:
|
||||
|
||||
is_test_release_path() {
|
||||
local lower="${1,,}"
|
||||
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == */test/* || "$lower" == */tests/* ]]
|
||||
local name="${lower##*/}"
|
||||
[[ "$lower" == test/* || "$lower" == tests/* || "$lower" == __tests__/* || "$lower" == */test/* || "$lower" == */tests/* || "$lower" == */__tests__/* || "$name" == test_* || "$name" == test-* || "$name" == spec_* || "$name" == spec-* || "$name" == *.test.* || "$name" == *.spec.* ]]
|
||||
}
|
||||
|
||||
generate_skillspector_report() {
|
||||
@@ -1146,9 +1325,9 @@ jobs:
|
||||
# --- Create zip preserving directory structure ---
|
||||
ZIP_NAME="${SKILL_NAME}-v${VERSION}.zip"
|
||||
(cd "$STAGING_DIR" && zip -qr "$OLDPWD/release-assets/$ZIP_NAME" .)
|
||||
if unzip -Z1 "release-assets/$ZIP_NAME" | grep -Eiq '(^|/)(test|tests)/'; then
|
||||
if unzip -Z1 "release-assets/$ZIP_NAME" | grep -Eiq '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.'; then
|
||||
echo "::error::Release archive contains test-only files: $ZIP_NAME"
|
||||
unzip -Z1 "release-assets/$ZIP_NAME" | grep -Ei '(^|/)(test|tests)/' || true
|
||||
unzip -Z1 "release-assets/$ZIP_NAME" | grep -Ei '(^|/)(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\.(test|spec)\.' || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -1424,8 +1603,6 @@ jobs:
|
||||
"",
|
||||
process.env.QUICK_INSTALL || "",
|
||||
"",
|
||||
"### SkillSpector Security Report",
|
||||
"",
|
||||
report,
|
||||
"",
|
||||
`Download the generated release-payload scan: [skillspector-report.md](https://github.com/${process.env.REPO}/releases/download/${process.env.TAG}/skillspector-report.md)`,
|
||||
@@ -1523,7 +1700,7 @@ jobs:
|
||||
|
||||
- name: Checkout
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true'
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Setup Node
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true'
|
||||
@@ -1533,51 +1710,12 @@ jobs:
|
||||
|
||||
- name: Install clawhub CLI
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
|
||||
run: npm install -g clawhub@${CLAWHUB_CLI_VERSION}
|
||||
run: bash scripts/ci/install_clawhub_cli.sh
|
||||
|
||||
- name: Patch clawhub publish payload workaround
|
||||
# Temporary: clawhub@0.7.0 publish payload is missing acceptLicenseTerms.
|
||||
# Idempotent compatibility guard: older clawhub@0.7.0 builds omitted acceptLicenseTerms.
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
|
||||
run: |
|
||||
node <<'NODE'
|
||||
const { execSync } = require("node:child_process");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const npmRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
|
||||
const publishScriptPath = path.join(
|
||||
npmRoot,
|
||||
"clawhub",
|
||||
"dist",
|
||||
"cli",
|
||||
"commands",
|
||||
"publish.js"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(publishScriptPath)) {
|
||||
throw new Error(`clawhub publish script not found: ${publishScriptPath}`);
|
||||
}
|
||||
|
||||
const original = fs.readFileSync(publishScriptPath, "utf8");
|
||||
if (original.includes("acceptLicenseTerms: true")) {
|
||||
console.log(`[patch-clawhub] Already patched: ${publishScriptPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const payloadPattern = /changelog,\r?\n(\s*)tags,/;
|
||||
if (!payloadPattern.test(original)) {
|
||||
throw new Error(
|
||||
`[patch-clawhub] Could not find expected publish payload pattern in ${publishScriptPath}`
|
||||
);
|
||||
}
|
||||
|
||||
const patched = original.replace(
|
||||
payloadPattern,
|
||||
(_, indent) => `changelog,\n${indent}acceptLicenseTerms: true,\n${indent}tags,`
|
||||
);
|
||||
fs.writeFileSync(publishScriptPath, patched, "utf8");
|
||||
console.log(`[patch-clawhub] Patched: ${publishScriptPath}`);
|
||||
NODE
|
||||
run: node scripts/ci/patch_clawhub_publish_payload.mjs
|
||||
|
||||
- name: Login to ClawHub
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
|
||||
@@ -1590,6 +1728,18 @@ jobs:
|
||||
CLAWHUB_DISABLE_TELEMETRY=1 CLAWHUB_SITE="$SITE" CLAWHUB_REGISTRY="$REGISTRY" \
|
||||
clawhub login --token "$CLAWHUB_TOKEN" --site "$SITE" --no-input
|
||||
|
||||
- name: Guard ClawHub slug ownership
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SITE=${CLAWHUB_SITE:-https://clawhub.ai}
|
||||
REGISTRY=${CLAWHUB_REGISTRY:-$SITE}
|
||||
export CLAWHUB_CONFIG_PATH="$HOME/.clawhub-ci/config.json"
|
||||
export CLAWHUB_SITE="$SITE"
|
||||
export CLAWHUB_REGISTRY="$REGISTRY"
|
||||
bash scripts/ci/guard_clawhub_slug_owner.sh \
|
||||
"${{ needs.release-tag.outputs.clawhub_slug }}"
|
||||
|
||||
- name: Guard duplicate ClawHub version
|
||||
if: needs.release-tag.outputs.publish_clawhub == 'true' && env.CLAWHUB_TOKEN != ''
|
||||
run: |
|
||||
@@ -1676,13 +1826,15 @@ jobs:
|
||||
echo "Parsed tag: skill=${SKILL_NAME}, version=${VERSION}"
|
||||
|
||||
- name: Checkout workflow helpers
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Prepare ClawHub slug helper
|
||||
run: cp scripts/ci/resolve_clawhub_slug.mjs "$RUNNER_TEMP/resolve_clawhub_slug.mjs"
|
||||
run: |
|
||||
cp scripts/ci/resolve_clawhub_slug.mjs "$RUNNER_TEMP/resolve_clawhub_slug.mjs"
|
||||
cp scripts/ci/skill_platforms.mjs "$RUNNER_TEMP/skill_platforms.mjs"
|
||||
|
||||
- name: Checkout tag
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
|
||||
@@ -1723,50 +1875,11 @@ jobs:
|
||||
run: node scripts/ci/validate_skill_install_docs.mjs --skills "${{ steps.parse.outputs.skill_path }}"
|
||||
|
||||
- name: Install clawhub CLI
|
||||
run: npm install -g clawhub@${CLAWHUB_CLI_VERSION}
|
||||
run: bash scripts/ci/install_clawhub_cli.sh
|
||||
|
||||
- name: Patch clawhub publish payload workaround
|
||||
# Temporary: clawhub@0.7.0 publish payload is missing acceptLicenseTerms.
|
||||
run: |
|
||||
node <<'NODE'
|
||||
const { execSync } = require("node:child_process");
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
|
||||
const npmRoot = execSync("npm root -g", { encoding: "utf8" }).trim();
|
||||
const publishScriptPath = path.join(
|
||||
npmRoot,
|
||||
"clawhub",
|
||||
"dist",
|
||||
"cli",
|
||||
"commands",
|
||||
"publish.js"
|
||||
);
|
||||
|
||||
if (!fs.existsSync(publishScriptPath)) {
|
||||
throw new Error(`clawhub publish script not found: ${publishScriptPath}`);
|
||||
}
|
||||
|
||||
const original = fs.readFileSync(publishScriptPath, "utf8");
|
||||
if (original.includes("acceptLicenseTerms: true")) {
|
||||
console.log(`[patch-clawhub] Already patched: ${publishScriptPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const payloadPattern = /changelog,\r?\n(\s*)tags,/;
|
||||
if (!payloadPattern.test(original)) {
|
||||
throw new Error(
|
||||
`[patch-clawhub] Could not find expected publish payload pattern in ${publishScriptPath}`
|
||||
);
|
||||
}
|
||||
|
||||
const patched = original.replace(
|
||||
payloadPattern,
|
||||
(_, indent) => `changelog,\n${indent}acceptLicenseTerms: true,\n${indent}tags,`
|
||||
);
|
||||
fs.writeFileSync(publishScriptPath, patched, "utf8");
|
||||
console.log(`[patch-clawhub] Patched: ${publishScriptPath}`);
|
||||
NODE
|
||||
# Idempotent compatibility guard: older clawhub@0.7.0 builds omitted acceptLicenseTerms.
|
||||
run: node scripts/ci/patch_clawhub_publish_payload.mjs
|
||||
|
||||
- name: Login to ClawHub
|
||||
run: |
|
||||
@@ -1783,6 +1896,17 @@ jobs:
|
||||
CLAWHUB_DISABLE_TELEMETRY=1 CLAWHUB_SITE="$SITE" CLAWHUB_REGISTRY="$REGISTRY" \
|
||||
clawhub login --token "$CLAWHUB_TOKEN" --site "$SITE" --no-input
|
||||
|
||||
- name: Guard ClawHub slug ownership
|
||||
run: |
|
||||
set -euo pipefail
|
||||
SITE=${CLAWHUB_SITE:-https://clawhub.ai}
|
||||
REGISTRY=${CLAWHUB_REGISTRY:-$SITE}
|
||||
export CLAWHUB_CONFIG_PATH="$HOME/.clawhub-ci/config.json"
|
||||
export CLAWHUB_SITE="$SITE"
|
||||
export CLAWHUB_REGISTRY="$REGISTRY"
|
||||
bash scripts/ci/guard_clawhub_slug_owner.sh \
|
||||
"${{ steps.publishable.outputs.clawhub_slug }}"
|
||||
|
||||
- name: Publish to ClawHub
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
|
||||
- name: Sync wiki folder to repository wiki
|
||||
env:
|
||||
|
||||
+2194
-2708
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
|
||||
K19pfVfv7qB1cqFPFTu69+sKLHIMIrmS7GeK4BZIlHzRvrLfRUuq/KftC8/CIWwvixVlBBm/iZlyfJ5sutoDDw==
|
||||
+496
-374
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
q1EyZ75QcdG2X6FVDkUoAyBtQE3ONA+7k9cmNFmXFgOOuGRPOpSDFUtbSvy86HPqnii26DMoeFJ1hatWJ0lBCQ==
|
||||
pmw3QutYARGuNH2evzHY/slVqxsrIGU+JrtS1hr1kOSqo1Md1aVBEA0tsNoQ+SkVjNohwGVk/61CcUxeW6WAAA==
|
||||
Generated
+573
-1019
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -31,13 +31,13 @@
|
||||
"@types/node": "^25.8.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.55.0",
|
||||
"@typescript-eslint/parser": "^8.58.1",
|
||||
"@vitejs/plugin-react": "^5.1.4",
|
||||
"@vitejs/plugin-react": "^6.0.2",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^7.0.1",
|
||||
"fast-check": "^4.7.0",
|
||||
"typescript": "~5.9.3",
|
||||
"vite": "^7.3.2"
|
||||
"vite": "^8.0.16"
|
||||
},
|
||||
"overrides": {
|
||||
"ajv": "6.14.0",
|
||||
|
||||
@@ -321,7 +321,14 @@ const fetchJson = async ({ repo, token, pathname, fetchImpl }) => {
|
||||
if (!response.ok) {
|
||||
const body = await response.text().catch(() => '');
|
||||
const suffix = body ? ` ${body.slice(0, 500)}` : '';
|
||||
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}`);
|
||||
const lacksPushAccess = response.status === 403
|
||||
&& /resource not accessible|must have push access/i.test(body);
|
||||
const hint = lacksPushAccess
|
||||
? ' Traffic endpoints require a token with push access to the repository; the Actions GITHUB_TOKEN is always rejected. Use a classic PAT with the repo scope or a fine-grained PAT with read access to Administration.'
|
||||
: response.status === 401
|
||||
? ' The token was rejected as invalid — it may be expired or revoked. Rotate the TRAFFIC_ARCHIVE_TOKEN secret.'
|
||||
: '';
|
||||
throw new Error(`GitHub traffic API request failed for ${repo}: ${url.pathname}${url.search} returned ${response.status}.${suffix}${hint}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 <target-clawhub-slug>" >&2
|
||||
}
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
usage
|
||||
exit 2
|
||||
fi
|
||||
|
||||
TARGET_SLUG="$1"
|
||||
SITE="${CLAWHUB_SITE:-https://clawhub.ai}"
|
||||
REGISTRY="${CLAWHUB_REGISTRY:-$SITE}"
|
||||
CONFIG_PATH="${CLAWHUB_CONFIG_PATH:-$HOME/.clawhub-ci/config.json}"
|
||||
|
||||
if [[ ! "$TARGET_SLUG" =~ ^[a-z0-9-]+$ ]]; then
|
||||
echo "::error::Invalid ClawHub slug for ownership guard: ${TARGET_SLUG}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$CONFIG_PATH" ]; then
|
||||
echo "::error::ClawHub config not found at ${CONFIG_PATH}. Run clawhub login before ownership guard."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TOKEN="$(jq -r '.token // empty' "$CONFIG_PATH")"
|
||||
if [ -z "$TOKEN" ]; then
|
||||
echo "::error::ClawHub token missing from ${CONFIG_PATH}. Run clawhub login before ownership guard."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TMP_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
|
||||
api_get() {
|
||||
local path="$1"
|
||||
local output_path="$2"
|
||||
local url="${REGISTRY%/}${path}"
|
||||
local http_status
|
||||
local curl_status
|
||||
|
||||
set +e
|
||||
http_status="$(
|
||||
curl --silent --show-error --location --max-time 15 \
|
||||
--header "Accept: application/json" \
|
||||
--header "Authorization: Bearer ${TOKEN}" \
|
||||
--output "$output_path" \
|
||||
--write-out "%{http_code}" \
|
||||
"$url"
|
||||
)"
|
||||
curl_status=$?
|
||||
set -e
|
||||
|
||||
if [ "$curl_status" -ne 0 ]; then
|
||||
echo "::error::Failed to call ClawHub API: ${url}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
printf '%s\n' "$http_status"
|
||||
}
|
||||
|
||||
whoami_json="$TMP_DIR/whoami.json"
|
||||
whoami_status="$(api_get "/api/v1/whoami" "$whoami_json")"
|
||||
if [ "$whoami_status" != "200" ]; then
|
||||
echo "::error::Failed to verify authenticated ClawHub publisher. HTTP ${whoami_status}."
|
||||
cat "$whoami_json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
publisher_handle="$(jq -r '.user.handle // empty' "$whoami_json")"
|
||||
if [ -z "$publisher_handle" ]; then
|
||||
echo "::error::Could not determine authenticated ClawHub publisher handle."
|
||||
cat "$whoami_json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target_json="$TMP_DIR/target.json"
|
||||
target_status="$(api_get "/api/v1/skills/${TARGET_SLUG}" "$target_json")"
|
||||
if [ "$target_status" = "404" ]; then
|
||||
echo "Target ClawHub slug ${TARGET_SLUG} is not currently published; authenticated publisher ${publisher_handle} may create it."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$target_status" != "200" ]; then
|
||||
echo "::error::Failed to inspect target ClawHub slug ${TARGET_SLUG}. HTTP ${target_status}."
|
||||
cat "$target_json"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
target_owner="$(jq -r '.owner.handle // .owner.displayName // empty' "$target_json")"
|
||||
if [ -z "$target_owner" ]; then
|
||||
echo "::error::Could not determine owner for existing ClawHub slug ${TARGET_SLUG}."
|
||||
echo "target owner: ${target_owner:-unknown}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$target_owner" != "$publisher_handle" ]; then
|
||||
echo "::error::Resolved ClawHub slug ${TARGET_SLUG} is already owned by ${target_owner}, but the authenticated publisher is ${publisher_handle}. Transfer or alias the registry slug before publishing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "ClawHub slug ownership guard passed: ${TARGET_SLUG} owned by authenticated publisher ${publisher_handle}."
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CLI_PREFIX="${CLAWHUB_CLI_PREFIX:-.github/clawhub-cli}"
|
||||
|
||||
npm ci --prefix "$CLI_PREFIX"
|
||||
|
||||
if [ -n "${GITHUB_PATH:-}" ]; then
|
||||
workspace="${GITHUB_WORKSPACE:-$(pwd)}"
|
||||
echo "${workspace}/${CLI_PREFIX}/node_modules/.bin" >> "$GITHUB_PATH"
|
||||
fi
|
||||
@@ -0,0 +1,35 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
const workspace = process.env.GITHUB_WORKSPACE || process.cwd();
|
||||
const npmRoot = path.join(workspace, ".github", "clawhub-cli", "node_modules");
|
||||
const publishScriptPath = path.join(
|
||||
npmRoot,
|
||||
"clawhub",
|
||||
"dist",
|
||||
"cli",
|
||||
"commands",
|
||||
"publish.js",
|
||||
);
|
||||
|
||||
if (!fs.existsSync(publishScriptPath)) {
|
||||
throw new Error(`clawhub publish script not found: ${publishScriptPath}`);
|
||||
}
|
||||
|
||||
const original = fs.readFileSync(publishScriptPath, "utf8");
|
||||
if (original.includes("acceptLicenseTerms: true")) {
|
||||
console.log(`[patch-clawhub] Already patched: ${publishScriptPath}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const payloadPattern = /changelog,\r?\n(\s*)tags,/;
|
||||
if (!payloadPattern.test(original)) {
|
||||
throw new Error(`[patch-clawhub] Could not find expected publish payload pattern in ${publishScriptPath}`);
|
||||
}
|
||||
|
||||
const patched = original.replace(
|
||||
payloadPattern,
|
||||
(_, indent) => `changelog,\n${indent}acceptLicenseTerms: true,\n${indent}tags,`,
|
||||
);
|
||||
fs.writeFileSync(publishScriptPath, patched, "utf8");
|
||||
console.log(`[patch-clawhub] Patched: ${publishScriptPath}`);
|
||||
@@ -43,14 +43,14 @@ export function resolveClawHubSlug({ name, platforms = [] }) {
|
||||
throw new Error(`Invalid skill name for ClawHub slug mapping: ${name}`);
|
||||
}
|
||||
|
||||
if (name.startsWith("clawsec-")) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (EXPLICIT_SLUGS.has(name)) {
|
||||
return EXPLICIT_SLUGS.get(name);
|
||||
}
|
||||
|
||||
if (name.startsWith("clawsec-")) {
|
||||
return name;
|
||||
}
|
||||
|
||||
if (PLATFORM_KEYS.some((platform) => name.startsWith(`${platform}-`))) {
|
||||
return `clawsec-${name}`;
|
||||
}
|
||||
|
||||
@@ -143,6 +143,8 @@ function changedSkillDirs({ root, base, head }) {
|
||||
`${base}...${head}`,
|
||||
"--",
|
||||
"skills/*/**",
|
||||
":(exclude)skills/clawsec-feed/advisories/feed.json",
|
||||
":(exclude)skills/clawsec-feed/advisories/feed.json.sig",
|
||||
":(exclude)skills/*/test/**",
|
||||
":(exclude)skills/*/tests/**",
|
||||
],
|
||||
|
||||
@@ -76,6 +76,40 @@ test('fetchGitHubTraffic requests the daily GitHub traffic endpoints with auth',
|
||||
assert.deepEqual(snapshot.clones.clones, responses[`/repos/${TEST_REPOSITORY}/traffic/clones?per=day`].clones);
|
||||
});
|
||||
|
||||
test('fetchGitHubTraffic explains traffic token requirements on 403', async () => {
|
||||
const fetchImpl = async () => new globalThis.Response(
|
||||
JSON.stringify({ message: 'Resource not accessible by integration' }),
|
||||
{ status: 403 },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
fetchGitHubTraffic({
|
||||
repo: TEST_REPOSITORY,
|
||||
token: 'installation-token',
|
||||
capturedAt,
|
||||
fetchImpl,
|
||||
}),
|
||||
/returned 403\..*push access/,
|
||||
);
|
||||
});
|
||||
|
||||
test('fetchGitHubTraffic flags invalid tokens on 401', async () => {
|
||||
const fetchImpl = async () => new globalThis.Response(
|
||||
JSON.stringify({ message: 'Bad credentials' }),
|
||||
{ status: 401 },
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
fetchGitHubTraffic({
|
||||
repo: TEST_REPOSITORY,
|
||||
token: 'expired-token',
|
||||
capturedAt,
|
||||
fetchImpl,
|
||||
}),
|
||||
/returned 401\..*expired or revoked/,
|
||||
);
|
||||
});
|
||||
|
||||
test('mergeTrafficArchive upserts daily views and clones without double-counting overlapping windows', () => {
|
||||
const archive = mergeTrafficArchive(
|
||||
{
|
||||
@@ -232,7 +266,8 @@ test('traffic archive workflow uses a daily schedule and a dedicated archive bra
|
||||
|
||||
assert.match(workflow, /cron:\s+'17 3 \* \* \*'/);
|
||||
assert.match(workflow, /TRAFFIC_ARCHIVE_BRANCH:\s+traffic-archive/);
|
||||
assert.match(workflow, /TRAFFIC_ARCHIVE_TOKEN/);
|
||||
assert.match(workflow, /GH_TRAFFIC_TOKEN:\s*\$\{\{\s*secrets\.TRAFFIC_ARCHIVE_TOKEN\b/);
|
||||
assert.doesNotMatch(workflow, /GH_TRAFFIC_TOKEN:[^\n]*github\.token/);
|
||||
assert.match(workflow, /node scripts\/archive-github-traffic\.mjs/);
|
||||
assert.match(workflow, /git add traffic\/archive\.json traffic\/summary\.json/);
|
||||
assert.match(workflow, /git rm --ignore-unmatch traffic\/README\.md/);
|
||||
|
||||
@@ -3,8 +3,18 @@ import { readFile } from 'node:fs/promises';
|
||||
|
||||
const workflowPath = new URL('../.github/workflows/skill-release.yml', import.meta.url);
|
||||
const ciWorkflowPath = new URL('../.github/workflows/ci.yml', import.meta.url);
|
||||
const clawhubLockPath = new URL('../.github/clawhub-cli/package-lock.json', import.meta.url);
|
||||
const validateSkillInstallDocsPath = new URL('./ci/validate_skill_install_docs.mjs', import.meta.url);
|
||||
const installClawhubCliPath = new URL('./ci/install_clawhub_cli.sh', import.meta.url);
|
||||
const patchClawhubPayloadPath = new URL('./ci/patch_clawhub_publish_payload.mjs', import.meta.url);
|
||||
const guardClawhubSlugOwnerPath = new URL('./ci/guard_clawhub_slug_owner.sh', import.meta.url);
|
||||
const workflow = await readFile(workflowPath, 'utf8');
|
||||
const ciWorkflow = await readFile(ciWorkflowPath, 'utf8');
|
||||
const clawhubLock = JSON.parse(await readFile(clawhubLockPath, 'utf8'));
|
||||
const validateSkillInstallDocs = await readFile(validateSkillInstallDocsPath, 'utf8');
|
||||
const installClawhubCli = await readFile(installClawhubCliPath, 'utf8');
|
||||
const patchClawhubPayload = await readFile(patchClawhubPayloadPath, 'utf8');
|
||||
const guardClawhubSlugOwner = await readFile(guardClawhubSlugOwnerPath, 'utf8');
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
@@ -12,6 +22,16 @@ assert.match(
|
||||
'Skill release workflow must run when any skill package file changes',
|
||||
);
|
||||
|
||||
for (const generatedFeedPath of [
|
||||
'skills/clawsec-feed/advisories/feed.json',
|
||||
'skills/clawsec-feed/advisories/feed.json.sig',
|
||||
]) {
|
||||
assert.ok(
|
||||
workflow.includes(` - '!${generatedFeedPath}'`),
|
||||
`Skill release workflow must not run for generated advisory mirror-only changes to ${generatedFeedPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/pull_request:[\s\S]*paths:[\s\S]*- '\.github\/workflows\/skill-release\.yml'[\s\S]*- 'scripts\/ci\/\*\*'/,
|
||||
@@ -30,8 +50,25 @@ assert.ok(
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
|
||||
'Skill release validation must ignore test-only skill changes while inspecting release-relevant skill files',
|
||||
/git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json\.sig'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
|
||||
'Skill release validation must ignore generated clawsec-feed advisory mirror and test-only changes while inspecting release-relevant skill files',
|
||||
);
|
||||
|
||||
for (const generatedFeedPath of [
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json',
|
||||
':(exclude)skills/clawsec-feed/advisories/feed.json.sig',
|
||||
]) {
|
||||
assert.ok(
|
||||
validateSkillInstallDocs.includes(`"${generatedFeedPath}"`),
|
||||
`Install-doc validation changed-skill detection must ignore generated advisory mirror-only changes to ${generatedFeedPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
assert.ok(
|
||||
workflow.includes('name = tolower($NF)')
|
||||
&& workflow.includes('name ~ /^(test|spec)[_-]/')
|
||||
&& workflow.includes('name ~ /\\.(test|spec)\\./'),
|
||||
'Skill release validation must filter test-named skill files such as scripts/test_*.py before selecting dry-run skill directories',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
@@ -88,10 +125,16 @@ assert.match(
|
||||
'Skill release workflow must generate a SkillSpector report for each released skill',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/"### SkillSpector Security Report"/,
|
||||
'GitHub release notes must not add a duplicate SkillSpector heading before the generated report',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/### SkillSpector Security Report[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
|
||||
'GitHub release notes must include a direct SkillSpector report link',
|
||||
/readFileSync\("release-assets\/skillspector-report\.md", "utf8"\)[\s\S]*report,[\s\S]*\[skillspector-report\.md\]\(https:\/\/github\.com\/\$\{process\.env\.REPO\}\/releases\/download\/\$\{process\.env\.TAG\}\/skillspector-report\.md\)/,
|
||||
'GitHub release notes must embed the generated SkillSpector report and include a direct report link',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
@@ -118,6 +161,20 @@ assert.match(
|
||||
'PR dry-run SkillSpector scan must target the staged release payload, not the source skill directory',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/Run release dry-run for changed skills[\s\S]*git diff --name-only "\$\{BASE_SHA\}\.\.\.\$\{HEAD_SHA\}" --[\s\S]*'skills\/\*\/\*\*'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json'[\s\S]*':\(exclude\)skills\/clawsec-feed\/advisories\/feed\.json\.sig'[\s\S]*':\(exclude\)skills\/\*\/test\/\*\*'[\s\S]*':\(exclude\)skills\/\*\/tests\/\*\*'/,
|
||||
'PR dry-run SkillSpector scan must run when any release-relevant skill package file changes except generated advisory mirror files',
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
workflow.includes('local name="${lower##*/}"')
|
||||
&& workflow.includes('"$name" == test_*')
|
||||
&& workflow.includes('"$name" == *.test.*')
|
||||
&& workflow.includes('(__tests__|test|tests)/|(^|/)(test|spec)[_-]|(^|/).*\\.(test|spec)\\.'),
|
||||
'Skill release archives must exclude test directories and test-named files from staged release payloads',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/generate_skillspector_report "\$\{skill_dir\}" "\$\{out_assets\}\/skillspector-report\.md"/,
|
||||
@@ -187,6 +244,61 @@ assert.match(
|
||||
'SkillSpector report must be included in the signed checksums manifest',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/Upload SkillSpector PR reports[\s\S]*actions\/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7\.0\.1[\s\S]*name: skillspector-pr-reports/,
|
||||
'PR dry-run must upload generated SkillSpector reports as workflow artifacts',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/comment-skillspector-report:[\s\S]*needs: release[\s\S]*issues: write[\s\S]*actions\/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8\.0\.1/,
|
||||
'Skill release workflow must download generated SkillSpector reports in a separate PR comment job with comment permissions',
|
||||
);
|
||||
|
||||
const commentJob = workflow.match(/[ ]{2}comment-skillspector-report:[\s\S]*?\n[ ]{2}[a-z][^:\n]*:/)?.[0] || "";
|
||||
assert.doesNotMatch(
|
||||
commentJob,
|
||||
/pull-requests: write/,
|
||||
'SkillSpector PR comment publishing should not request redundant pull-requests write permissions',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/comment-skillspector-report:[\s\S]*if: always\(\) && github\.event_name == 'pull_request' && needs\.release\.result != 'cancelled'[\s\S]*Download SkillSpector reports[\s\S]*continue-on-error: true/,
|
||||
'SkillSpector PR comments must still run when the release dry-run produced reports but the release job failed later',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/Comment SkillSpector reports[\s\S]*continue-on-error: true[\s\S]*actions\/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9\.0\.0/,
|
||||
'SkillSpector PR comment publishing must not fail the release dry-run check',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/function sanitizeReportForComment\(report\)[\s\S]*code block omitted from PR comment[\s\S]*inline snippet omitted[\s\S]*redacted-email[\s\S]*redacted-token/,
|
||||
'SkillSpector PR comments must sanitize raw report content before posting to the PR',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/const sanitizedReport = sanitizeReportForComment\(report\);[\s\S]*`\$\{marker\}\\n\$\{sanitizedReport\}/,
|
||||
'SkillSpector PR comments must use the sanitized report body, not the raw artifact text',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/`\$\{marker\}\\n\$\{report\.trimEnd\(\)\}/,
|
||||
'SkillSpector PR comments must not post report.trimEnd() verbatim',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/clawsec-skillspector-report:\$\{tag\}[\s\S]*github\.rest\.issues\.updateComment[\s\S]*github\.rest\.issues\.createComment/,
|
||||
'SkillSpector PR comments must use stable per-skill markers and update existing comments before creating new ones',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/Simulate tag release build/,
|
||||
@@ -218,8 +330,8 @@ assert.match(
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/cp scripts\/ci\/resolve_clawhub_slug\.mjs "\$RUNNER_TEMP\/resolve_clawhub_slug\.mjs"/,
|
||||
'Manual ClawHub republish must preserve the current slug helper before checking out an older release tag',
|
||||
/cp scripts\/ci\/resolve_clawhub_slug\.mjs "\$RUNNER_TEMP\/resolve_clawhub_slug\.mjs"[\s\S]*cp scripts\/ci\/skill_platforms\.mjs "\$RUNNER_TEMP\/skill_platforms\.mjs"/,
|
||||
'Manual ClawHub republish must preserve the current slug helper and its local module dependency before checking out an older release tag',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
@@ -246,6 +358,129 @@ assert.match(
|
||||
'ClawHub publish must use the resolved ClawHub slug',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/clawhub publish "\$SKILL_PATH"[\s\S]*--slug "\$CLAWHUB_SLUG"/,
|
||||
'ClawHub publish must use the resolved ClawHub slug',
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
workflow.match(/bash scripts\/ci\/install_clawhub_cli\.sh/g)?.length,
|
||||
2,
|
||||
'ClawHub publish and republish jobs must share the same pinned CLI installer',
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
workflow.match(/node scripts\/ci\/patch_clawhub_publish_payload\.mjs/g)?.length,
|
||||
2,
|
||||
'ClawHub publish and republish jobs must share the same payload patch helper',
|
||||
);
|
||||
|
||||
assert.equal(
|
||||
workflow.match(/bash scripts\/ci\/guard_clawhub_slug_owner\.sh/g)?.length,
|
||||
2,
|
||||
'ClawHub publish and republish jobs must guard mapped slug ownership before publishing',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/npm ci --prefix \.github\/clawhub-cli/,
|
||||
'ClawHub CLI installation must not be duplicated inline in the workflow',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/node <<'NODE'[\s\S]*acceptLicenseTerms: true/,
|
||||
'ClawHub payload patching must not be duplicated inline in the workflow',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
installClawhubCli,
|
||||
/npm ci --prefix "\$CLI_PREFIX"/,
|
||||
'ClawHub CLI installer must install from the committed lockfile prefix',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
installClawhubCli,
|
||||
/aws codeartifact login|AWS credentials are required/,
|
||||
'ClawHub CLI installer must not require AWS secrets that are not configured for release workflows',
|
||||
);
|
||||
|
||||
const clawhubLockResolvedUrls = Object.values(clawhubLock.packages ?? {})
|
||||
.map((entry) => entry.resolved)
|
||||
.filter(Boolean);
|
||||
assert.ok(clawhubLockResolvedUrls.length > 0, 'ClawHub CLI lockfile must contain resolved tarball URLs');
|
||||
assert.ok(
|
||||
clawhubLockResolvedUrls.every((url) => url.startsWith('https://registry.npmjs.org/')),
|
||||
'ClawHub CLI lockfile must use public npm tarballs because release workflows do not have AWS CodeArtifact secrets',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
installClawhubCli,
|
||||
/"\$\{workspace\}\/\$\{CLI_PREFIX\}\/node_modules\/\.bin" >> "\$GITHUB_PATH"/,
|
||||
'ClawHub CLI installer must expose the pinned clawhub binary on GITHUB_PATH',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
patchClawhubPayload,
|
||||
/const payloadPattern = \/changelog,\\r\?\\n\(\\s\*\)tags,\/;/,
|
||||
'ClawHub payload patch helper must target the expected publish payload shape',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
patchClawhubPayload,
|
||||
/acceptLicenseTerms: true/,
|
||||
'ClawHub payload patch helper must preserve the acceptLicenseTerms workaround',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
patchClawhubPayload,
|
||||
/Already patched/,
|
||||
'ClawHub payload patch helper must stay idempotent when the pinned CLI already includes acceptLicenseTerms',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
guardClawhubSlugOwner,
|
||||
/api_get "\/api\/v1\/whoami" "\$whoami_json"/,
|
||||
'ClawHub slug ownership guard must verify the authenticated publisher through the ClawHub API',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
guardClawhubSlugOwner,
|
||||
/api_get "\/api\/v1\/skills\/\$\{TARGET_SLUG\}" "\$target_json"/,
|
||||
'ClawHub slug ownership guard must inspect the resolved publish slug through the ClawHub API',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
guardClawhubSlugOwner,
|
||||
/\[ "\$target_status" = "404" \]/,
|
||||
'ClawHub slug ownership guard must treat HTTP 404 as the structured unpublished-slug signal',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
guardClawhubSlugOwner,
|
||||
/\[ "\$target_owner" != "\$publisher_handle" \]/,
|
||||
'ClawHub slug ownership guard must reject slugs owned by a different authenticated registry publisher',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
guardClawhubSlugOwner,
|
||||
/SOURCE_SLUG|source_owner|grep -Eqi[\s\S]*Skill not found/,
|
||||
'ClawHub slug ownership guard must not inspect raw source names or depend on stderr wording',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/SITE=\$\{CLAWHUB_SITE:-https:\/\/clawhub\.ai\}[\s\S]*REGISTRY=\$\{CLAWHUB_REGISTRY:-\$SITE\}[\s\S]*export CLAWHUB_CONFIG_PATH="\$HOME\/\.clawhub-ci\/config\.json"[\s\S]*export CLAWHUB_SITE="\$SITE"[\s\S]*export CLAWHUB_REGISTRY="\$REGISTRY"[\s\S]*bash scripts\/ci\/guard_clawhub_slug_owner\.sh[\s\S]*\$\{\{ needs\.release-tag\.outputs\.clawhub_slug \}\}/,
|
||||
'ClawHub publish job must guard the resolved publish slug with the authenticated ClawHub config path',
|
||||
);
|
||||
|
||||
assert.match(
|
||||
workflow,
|
||||
/SITE=\$\{CLAWHUB_SITE:-https:\/\/clawhub\.ai\}[\s\S]*REGISTRY=\$\{CLAWHUB_REGISTRY:-\$SITE\}[\s\S]*export CLAWHUB_CONFIG_PATH="\$HOME\/\.clawhub-ci\/config\.json"[\s\S]*export CLAWHUB_SITE="\$SITE"[\s\S]*export CLAWHUB_REGISTRY="\$REGISTRY"[\s\S]*bash scripts\/ci\/guard_clawhub_slug_owner\.sh[\s\S]*\$\{\{ steps\.publishable\.outputs\.clawhub_slug \}\}/,
|
||||
'ClawHub republish job must guard the resolved publish slug with the authenticated ClawHub config path',
|
||||
);
|
||||
|
||||
assert.doesNotMatch(
|
||||
workflow,
|
||||
/clawhub inspect "\$SKILL_NAME" --version "\$VERSION" --json/,
|
||||
|
||||
@@ -140,16 +140,16 @@ writeFileSync(process.argv[outputIndex + 1], "# Fake SkillSpector Report\\n\\nNo
|
||||
await runSimulation({
|
||||
skillDir: "skills/clawsec-suite",
|
||||
outputDir: path.join(tempRoot, "stable"),
|
||||
expectedOriginal: "0.1.10",
|
||||
expectedSimulated: "0.1.11",
|
||||
expectedOriginal: "0.1.11",
|
||||
expectedSimulated: "0.1.12",
|
||||
expectedAgent: "openclaw",
|
||||
});
|
||||
|
||||
await runSimulation({
|
||||
skillDir: "skills/hermes-traffic-guardian",
|
||||
outputDir: path.join(tempRoot, "beta"),
|
||||
expectedOriginal: "0.0.1-beta3",
|
||||
expectedSimulated: "0.0.1-beta4",
|
||||
expectedOriginal: "0.0.1-beta4",
|
||||
expectedSimulated: "0.0.1-beta5",
|
||||
expectedAgent: "hermes-agent",
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ function runTrustPacket(skillDir, targetDir, tag) {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = runTrustPacket("skills/clawsec-suite", outputDir, "clawsec-suite-v0.1.10");
|
||||
const result = runTrustPacket("skills/clawsec-suite", outputDir, "clawsec-suite-v0.1.11");
|
||||
|
||||
assert.equal(
|
||||
result.status,
|
||||
@@ -41,10 +41,10 @@ try {
|
||||
assert.match(skillCard, /## License\/Terms of Use/);
|
||||
assert.match(skillCard, /AGPL-3\.0-or-later/);
|
||||
assert.match(skillCard, /skillspector-report\.md/);
|
||||
assert.match(skillCard, /clawsec-suite-v0\.1\.10/);
|
||||
assert.match(skillCard, /clawsec-suite-v0\.1\.11/);
|
||||
|
||||
assert.equal(permissions.skill, "clawsec-suite");
|
||||
assert.equal(permissions.version, "0.1.10");
|
||||
assert.equal(permissions.version, "0.1.11");
|
||||
assert.equal(permissions.platform, "openclaw");
|
||||
assert.deepEqual(
|
||||
permissions.required_binaries,
|
||||
@@ -62,7 +62,7 @@ try {
|
||||
const hermesResult = runTrustPacket(
|
||||
"skills/hermes-attestation-guardian",
|
||||
hermesOutputDir,
|
||||
"hermes-attestation-guardian-v0.1.4",
|
||||
"hermes-attestation-guardian-v0.1.5",
|
||||
);
|
||||
assert.equal(
|
||||
hermesResult.status,
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.7] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.6] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawsec-clawhub-checker
|
||||
version: 0.0.6
|
||||
version: 0.0.7
|
||||
description: ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.
|
||||
homepage: https://clawsec.prompt.security
|
||||
clawdis:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawsec-clawhub-checker",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"description": "ClawHub reputation checker for clawsec-suite. Adds a standalone reputation gate before guarded skill installation.",
|
||||
"author": "abutbul",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.10] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.9] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawsec-feed
|
||||
version: 0.0.9
|
||||
version: 0.0.10
|
||||
description: Security advisory feed package for OpenClaw-related threats and vulnerabilities. The upstream feed is updated daily; local automation is handled by clawsec-suite or the operator.
|
||||
homepage: https://clawsec.prompt.security
|
||||
metadata: {"openclaw":{"emoji":"📡","category":"security"}}
|
||||
|
||||
+2194
-2708
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
agiAAFvzM1vNHxH2+bGtyeKqFScLWJHnNreBcPpTODUqD0xqFi0cnyP/ZaZX+Rsw1Y9uZ7pGdFdA93pD4lh2BQ==
|
||||
K19pfVfv7qB1cqFPFTu69+sKLHIMIrmS7GeK4BZIlHzRvrLfRUuq/KftC8/CIWwvixVlBBm/iZlyfJ5sutoDDw==
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawsec-feed",
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.10",
|
||||
"description": "Security advisory feed monitoring for AI agents. Subscribe to community-driven threat intelligence.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.9] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.8] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawsec-nanoclaw
|
||||
version: 0.0.8
|
||||
version: 0.0.9
|
||||
description: Use when checking for security vulnerabilities in NanoClaw skills, before installing new skills, or when asked about security advisories affecting the bot
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawsec-nanoclaw",
|
||||
"version": "0.0.8",
|
||||
"version": "0.0.9",
|
||||
"description": "ClawSec security suite for NanoClaw - Advisory feed monitoring, MCP tools for vulnerability checking, and Ed25519 signature verification for containerized WhatsApp bot agents",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.6] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.5] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawsec-scanner
|
||||
version: 0.0.5
|
||||
version: 0.0.6
|
||||
description: Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks.
|
||||
homepage: https://clawsec.prompt.security
|
||||
clawdis:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawsec-scanner",
|
||||
"version": "0.0.5",
|
||||
"version": "0.0.6",
|
||||
"description": "Automated vulnerability scanner for agent platforms. Performs dependency scanning (npm audit, pip-audit), multi-database CVE lookup (OSV, NVD, GitHub Advisory), SAST analysis (Semgrep, Bandit), and agent-specific static hook inspection for OpenClaw hooks.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.11] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.1.10] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawsec-suite
|
||||
version: 0.1.10
|
||||
version: 0.1.11
|
||||
description: ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills.
|
||||
homepage: https://clawsec.prompt.security
|
||||
clawdis:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawsec-suite",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"description": "ClawSec suite manager with embedded advisory-feed monitoring, cryptographic signature verification, approval-gated malicious-skill response, and guided setup for additional security skills.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.8] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.7] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: clawtributor
|
||||
version: 0.0.7
|
||||
version: 0.0.8
|
||||
description: Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.
|
||||
homepage: https://clawsec.prompt.security
|
||||
platforms:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clawtributor",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "Harness-neutral community incident reporting for AI agents. Contribute to collective security by reporting threats.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.5] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.1.4] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: hermes-attestation-guardian
|
||||
version: 0.1.4
|
||||
version: 0.1.5
|
||||
description: Hermes-only runtime security attestation and drift detection skill for operator-managed Hermes infrastructure.
|
||||
homepage: https://clawsec.prompt.security
|
||||
hermes:
|
||||
@@ -31,7 +31,7 @@ For standalone installs, verify the signed release manifest before trusting `SKI
|
||||
set -euo pipefail
|
||||
|
||||
SKILL_NAME="hermes-attestation-guardian"
|
||||
VERSION="0.1.4"
|
||||
VERSION="0.1.5"
|
||||
REPO="prompt-security/clawsec"
|
||||
TAG="${SKILL_NAME}-v${VERSION}"
|
||||
BASE="https://github.com/${REPO}/releases/download/${TAG}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hermes-attestation-guardian",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"description": "Hermes-only runtime security attestation and drift detection skill. Generates deterministic posture artifacts, verifies integrity fail-closed, and classifies baseline drift severity.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.1-beta4] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.1-beta3] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: hermes-traffic-guardian
|
||||
version: 0.0.1-beta3
|
||||
version: 0.0.1-beta4
|
||||
description: Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hermes-traffic-guardian",
|
||||
"version": "0.0.1-beta3",
|
||||
"version": "0.0.1-beta4",
|
||||
"description": "Hermes runtime traffic monitoring baseline for opt-in proxy inspection, egress detection, and attestation-aware traffic posture.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.1-beta4] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.1-beta3] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: nanoclaw-traffic-guardian
|
||||
version: 0.0.1-beta3
|
||||
version: 0.0.1-beta4
|
||||
description: NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "nanoclaw-traffic-guardian",
|
||||
"version": "0.0.1-beta3",
|
||||
"version": "0.0.1-beta4",
|
||||
"description": "NanoClaw runtime traffic monitoring baseline for host-side proxy inspection with container-safe MCP and IPC status surfaces.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.1.8] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.1.7] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: openclaw-audit-watchdog
|
||||
version: 0.1.7
|
||||
version: 0.1.8
|
||||
description: Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. Runs deep audits, creates or updates a recurring cron job, and sends formatted reports to configured recipients.
|
||||
homepage: https://clawsec.prompt.security
|
||||
metadata:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openclaw-audit-watchdog",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.8",
|
||||
"description": "Automated daily security audits for OpenClaw agents with DM delivery and optional email reporting. Creates or updates an unattended cron job and sends formatted reports to configured recipients.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.1-beta4] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.1-beta3] - 2026-06-10
|
||||
|
||||
### Security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: openclaw-traffic-guardian
|
||||
version: 0.0.1-beta3
|
||||
version: 0.0.1-beta4
|
||||
description: OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openclaw-traffic-guardian",
|
||||
"version": "0.0.1-beta3",
|
||||
"version": "0.0.1-beta4",
|
||||
"description": "OpenClaw runtime traffic monitoring baseline for opt-in HTTP/HTTPS proxy inspection, egress detection, inbound injection detection, and social-account policy review.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.5] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.4] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: picoclaw-security-guardian
|
||||
version: 0.0.4
|
||||
version: 0.0.5
|
||||
description: Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picoclaw-security-guardian",
|
||||
"version": "0.0.4",
|
||||
"version": "0.0.5",
|
||||
"description": "Picoclaw security posture skill with advisory awareness, configuration drift detection, and supply-chain verification guidance.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.4] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.3] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: picoclaw-self-pen-testing
|
||||
version: 0.0.3
|
||||
version: 0.0.4
|
||||
description: Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picoclaw-self-pen-testing",
|
||||
"version": "0.0.3",
|
||||
"version": "0.0.4",
|
||||
"description": "Picoclaw-only local posture-review skill focused on read-only findings and safe operator remediation guidance.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.1-beta4] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.1-beta3] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: picoclaw-traffic-guardian
|
||||
version: 0.0.1-beta3
|
||||
version: 0.0.1-beta4
|
||||
description: Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.
|
||||
homepage: https://clawsec.prompt.security
|
||||
author: prompt-security
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "picoclaw-traffic-guardian",
|
||||
"version": "0.0.1-beta3",
|
||||
"version": "0.0.1-beta4",
|
||||
"description": "Picoclaw runtime traffic monitoring baseline for lightweight AI gateway proxy inspection, egress detection, and posture integration.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Changelog
|
||||
|
||||
## [0.0.8] - 2026-06-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Re-released skill metadata to publish through the updated ClawHub pipeline without runtime changes.
|
||||
|
||||
## [0.0.7] - 2026-06-10
|
||||
|
||||
### Changed
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: soul-guardian
|
||||
version: 0.0.7
|
||||
version: 0.0.8
|
||||
description: Drift detection + baseline integrity guard for agent workspace files with automatic alerting support
|
||||
homepage: https://clawsec.prompt.security
|
||||
metadata: {"openclaw":{"emoji":"👻","category":"security"}}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soul-guardian",
|
||||
"version": "0.0.7",
|
||||
"version": "0.0.8",
|
||||
"description": "Drift detection and baseline integrity guard for agent workspace prompt files. Auto-restore critical files with tamper-evident audit logging.",
|
||||
"author": "prompt-security",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
| GitHub API | Deploy/release workflows | Discover releases, download assets, publish outputs. |
|
||||
| GitHub Pages | Deploy workflow | Serve static site and mirrored artifacts. |
|
||||
| ClawHub CLI/registry | Install scripts + optional publish jobs | Install and publish skills. |
|
||||
| [NVIDIA SkillSpector](https://github.com/NVIDIA/SkillSpector) | Skill release workflow | Scan staged skill release payloads and produce Markdown release evidence. |
|
||||
| Optional local SMTP/sendmail | `openclaw-audit-watchdog` scripts | Deliver audit reports by email. |
|
||||
|
||||
## Development Tools
|
||||
@@ -46,6 +47,7 @@
|
||||
| Bandit | `bandit -r utils/ -ll` | Python security checks. |
|
||||
| Trivy | Workflow + optional local run | FS/config vulnerability scans. |
|
||||
| Gitleaks | `scripts/prepare-to-push.sh` optional local run | Secret leak detection before push. |
|
||||
| SkillSpector | `.github/workflows/skill-release.yml` | Release-payload scanner used for PR comments and signed release artifacts. |
|
||||
|
||||
## Example Snippets
|
||||
```json
|
||||
@@ -83,6 +85,7 @@ skips = ["B101"]
|
||||
- PR validation enforces version parity between `skill.json` and `SKILL.md` frontmatter for bumped skills.
|
||||
- The public skills index keeps latest discovered version per skill for UI display.
|
||||
- Signed artifact manifests (`checksums.json`) are versioned per release and include file hashes and URLs.
|
||||
- SkillSpector reports are generated per release payload and included in signed artifact manifests.
|
||||
|
||||
## Source References
|
||||
- package.json
|
||||
|
||||
+2
-1
@@ -15,6 +15,7 @@
|
||||
| --- | --- |
|
||||
| Skill Tag | Git tag formatted as `<skill>-v<semver>` used by release automation. |
|
||||
| Release Assets | Files attached to GitHub release (zip, `skill.json`, checksums, signatures). |
|
||||
| SkillSpector Report | Markdown security scan evidence generated from a staged skill release payload. |
|
||||
| Catalog Index | `public/skills/index.json`, generated list consumed by web catalog. |
|
||||
| Embedded Components | Capability bundle from one skill included in another (for example feed embedded in suite). |
|
||||
|
||||
@@ -39,7 +40,7 @@
|
||||
| --- | --- |
|
||||
| Poll NVD CVEs Workflow | Scheduled workflow that fetches and transforms NVD CVEs into advisories. |
|
||||
| Community Advisory Workflow | Issue-label-triggered workflow that publishes approved community advisories. |
|
||||
| Skill Release Workflow | Tag-triggered packaging/signing/publishing pipeline for skills. |
|
||||
| Skill Release Workflow | PR and tag-triggered packaging/signing/publishing pipeline for skills. |
|
||||
| Deploy Pages Workflow | Workflow that builds site assets and mirrors release/advisory artifacts. |
|
||||
|
||||
## Source References
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Track translation coverage and freshness versus English source docs.
|
||||
|
||||
_Last updated: 2026-04-27_
|
||||
_Last updated: 2026-06-14_
|
||||
|
||||
## README Coverage
|
||||
|
||||
@@ -24,6 +24,12 @@ _Last updated: 2026-04-27_
|
||||
| `wiki/testing.md` | — | pending |
|
||||
| `wiki/workflow.md` | — | pending |
|
||||
|
||||
## English Source Freshness Notes
|
||||
|
||||
| Date | Changed pages | Translation impact |
|
||||
| --- | --- | --- |
|
||||
| 2026-06-14 | `wiki/workflow.md`, `wiki/modules/automation-release.md`, `wiki/security-signing-runbook.md`, `wiki/dependencies.md`, `wiki/glossary.md` | Added SkillSpector release-pipeline documentation, signed-report behavior, and PR comment behavior. Translation refresh pending. |
|
||||
|
||||
## Wiki Coverage (KO)
|
||||
|
||||
| Source page | Korean page | Status |
|
||||
|
||||
@@ -19,11 +19,38 @@ This module intentionally focuses on automation/release-specific workflow behavi
|
||||
When a skill is tagged (for example, `soul-guardian-v1.0.0`), the pipeline:
|
||||
1. Validates `skill.json` version/tag alignment.
|
||||
2. Enforces signing-key consistency against canonical repo key material.
|
||||
3. Generates `checksums.json` for SBOM files.
|
||||
4. Signs and verifies release checksum artifacts.
|
||||
5. Publishes GitHub Release assets.
|
||||
6. Supersedes older releases within the same major version (tags remain).
|
||||
7. Triggers website catalog refresh.
|
||||
3. Stages the release payload from SBOM-scoped files and root skill docs.
|
||||
4. Generates release trust packet files, install instructions, and a SkillSpector security report.
|
||||
5. Generates `checksums.json` for the archive and release assets.
|
||||
6. Signs and verifies release checksum artifacts.
|
||||
7. Publishes GitHub Release assets.
|
||||
8. Supersedes older releases within the same major version (tags remain).
|
||||
9. Triggers website catalog refresh.
|
||||
|
||||
### PR dry-run behavior
|
||||
PRs that touch skill packages run the release workflow in validation mode:
|
||||
- `validate-pr-version-sync` checks changed skill metadata and documentation parity.
|
||||
- `release` builds dry-run release assets for changed release-relevant skill files.
|
||||
- `comment-skillspector-report` posts a sanitized SkillSpector summary back to the PR when reports are available.
|
||||
- `simulate-tag-release-build` exercises the tag-release builder across skills without publishing.
|
||||
|
||||
The PR path exists to catch packaging, signing, and release-evidence regressions before a maintainer pushes a real release tag.
|
||||
|
||||
### SkillSpector release evidence
|
||||
The pipeline installs [NVIDIA SkillSpector](https://github.com/NVIDIA/SkillSpector) inside GitHub Actions and runs:
|
||||
|
||||
```bash
|
||||
skillspector scan <staged-release-payload> --no-llm --format markdown --output skillspector-report.md
|
||||
```
|
||||
|
||||
The scan target is the staged payload, not the raw `skills/<name>/` source directory. That matters because release evidence should describe what users install, while source-only tests and fixtures stay outside the packaged payload.
|
||||
|
||||
SkillSpector output is used in three places:
|
||||
- PR dry-run artifact: `skillspector-pr-reports`
|
||||
- GitHub release asset: `skillspector-report.md`
|
||||
- Signed checksum manifest: `checksums.json` includes the SkillSpector report hash
|
||||
|
||||
PR comments intentionally use a sanitized summary. Raw code blocks, inline snippets, emails, and token-like values are omitted from the comment body, and reviewers can download the workflow artifact when they need the full report.
|
||||
|
||||
### Signing-key consistency guardrails
|
||||
Guardrail script:
|
||||
@@ -40,9 +67,16 @@ Enforced in:
|
||||
|
||||
### Release artifacts
|
||||
Each skill release includes:
|
||||
- `<skill>-v<version>.zip`
|
||||
- `checksums.json`
|
||||
- `checksums.sig`
|
||||
- `signing-public.pem`
|
||||
- `skill.json`
|
||||
- `SKILL.md`
|
||||
- `skill-card.md`
|
||||
- `permissions.json`
|
||||
- `install.md`
|
||||
- `skillspector-report.md`
|
||||
- Additional SBOM-scoped files
|
||||
|
||||
Operational docs:
|
||||
@@ -58,6 +92,7 @@ Operational docs:
|
||||
- `.github/workflows/deploy-pages.yml`: site build + asset mirroring to GitHub Pages.
|
||||
- `.github/workflows/wiki-sync.yml`: syncs repository `wiki/` into GitHub Wiki.
|
||||
- `.github/actions/sign-and-verify/action.yml`: shared Ed25519 sign/verify composite action.
|
||||
- `https://github.com/NVIDIA/SkillSpector`: upstream SkillSpector scanner installed by the release workflow.
|
||||
- `scripts/prepare-to-push.sh`: local CI-like quality gate.
|
||||
- `scripts/release-skill.sh`: manual helper for version bump + tag workflow.
|
||||
|
||||
|
||||
@@ -141,11 +141,18 @@ Current behavior:
|
||||
Current release generator:
|
||||
- `.github/workflows/skill-release.yml`
|
||||
|
||||
Current behavior:
|
||||
Detailed packaging and SkillSpector behavior lives in [Automation and Release Pipelines](modules/automation-release.md). This runbook only records the signing controls operators must verify.
|
||||
|
||||
Signing controls:
|
||||
- creates `checksums.json`, signs it as `checksums.sig`, and verifies signature before publish
|
||||
- includes `signing-public.pem` in release assets
|
||||
- validates generated public-key fingerprint against canonical key material
|
||||
|
||||
Operator review points:
|
||||
- verify `checksums.json` includes the release-evidence files documented in `wiki/modules/automation-release.md`
|
||||
- verify `checksums.sig` validates against `signing-public.pem`
|
||||
- review the release workflow run and PR evidence links before pushing or approving follow-up release tags
|
||||
|
||||
## 8) Rotation policy and runbook
|
||||
|
||||
### Rotation cadence
|
||||
|
||||
+7
-1
@@ -32,11 +32,16 @@
|
||||
|
||||
## Release Workflow Details
|
||||
- Version bump and docs parity are enforced for PR/tag paths.
|
||||
- Skill packaging includes SBOM-declared files and integrity manifests.
|
||||
- PR runs validate changed skill packages with a dry-run build before anything is published.
|
||||
- Tag pushes matching `<skill>-v<semver>` build the real release payload, sign `checksums.json`, verify the signature, and publish GitHub Release assets.
|
||||
- Skill packaging includes SBOM-declared files, release trust packet files, install instructions, security scan evidence, and integrity manifests.
|
||||
- `checksums.json` is signed and immediately verified in workflow execution.
|
||||
- Optional publish-to-ClawHub job runs after successful GitHub release when configured.
|
||||
- Older releases within same major line can be superseded/deleted by automation.
|
||||
|
||||
## SkillSpector Release Evidence
|
||||
Detailed SkillSpector release behavior lives in [Automation and Release Pipelines](modules/automation-release.md). Keep the detailed scanner command, staged-payload rules, PR comment behavior, and release-asset list there so scanner changes have one primary documentation owner.
|
||||
|
||||
## Advisory Workflow Details
|
||||
- NVD workflow determines incremental window from previous feed `updated` timestamp.
|
||||
- Transform phase maps CVE metrics to severity/type and normalizes affected targets.
|
||||
@@ -74,6 +79,7 @@ on:
|
||||
- .github/workflows/poll-nvd-cves.yml
|
||||
- .github/workflows/community-advisory.yml
|
||||
- .github/workflows/skill-release.yml
|
||||
- https://github.com/NVIDIA/SkillSpector
|
||||
- .github/workflows/deploy-pages.yml
|
||||
- .github/workflows/pages-verify.yml
|
||||
- .github/workflows/wiki-sync.yml
|
||||
|
||||
Reference in New Issue
Block a user