@jini-ai/agent-plugins 0.6.16 → 0.6.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/CHANGELOG.md +35 -1
  2. package/NOTICE +14 -0
  3. package/README.md +35 -14
  4. package/charts-echarts/plugin.json +39 -0
  5. package/charts-echarts/skills/charts-echarts/SKILL.md +110 -0
  6. package/charts-recharts/plugin.json +32 -0
  7. package/charts-recharts/skills/charts-recharts/SKILL.md +108 -0
  8. package/cloudflare/NOTES.md +16 -0
  9. package/cloudflare/form-destinations/cloudflare-d1.mjs +9 -0
  10. package/cloudflare/lib/api.mjs +96 -0
  11. package/cloudflare/lib/assets.mjs +65 -0
  12. package/cloudflare/lib/dns.mjs +32 -0
  13. package/cloudflare/lib/domain-inventory.mjs +61 -0
  14. package/cloudflare/lib/domain-status.mjs +48 -0
  15. package/cloudflare/lib/domains.mjs +95 -0
  16. package/cloudflare/lib/forms.mjs +254 -0
  17. package/cloudflare/lib/pages-upload.mjs +51 -0
  18. package/cloudflare/lib/target.mjs +165 -0
  19. package/cloudflare/lib/workers-upload.mjs +48 -0
  20. package/cloudflare/lib/zones.mjs +25 -0
  21. package/cloudflare/mcp.json +4 -0
  22. package/cloudflare/plugin.json +96 -0
  23. package/cloudflare/skills/cloudflare/SKILL.md +57 -0
  24. package/cloudflare/skills/cloudflare/references/domains.md +31 -0
  25. package/cloudflare/skills/cloudflare/references/pages.md +18 -0
  26. package/cloudflare/skills/cloudflare/references/permissions.md +29 -0
  27. package/cloudflare/skills/cloudflare/references/troubleshooting.md +20 -0
  28. package/cloudflare/skills/cloudflare/references/workers.md +42 -0
  29. package/cloudflare/targets/cloudflare-pages.mjs +12 -0
  30. package/cloudflare/targets/cloudflare-workers.mjs +17 -0
  31. package/cloudflare/tovu-consent.json +6 -0
  32. package/cloudflare/tovu-deploy-targets.json +630 -0
  33. package/cloudflare/tovu-form-destinations.json +12 -0
  34. package/cloudflare/worker/forms-receiver.mjs +249 -0
  35. package/composio/mcp.json +10 -0
  36. package/composio/plugin.json +68 -0
  37. package/composio/skills/composio/SKILL.md +85 -0
  38. package/dist/core/identity-namespace.d.ts +12 -0
  39. package/dist/core/identity-namespace.js +20 -0
  40. package/dist/folder-zip-node.js +29 -7
  41. package/dist/lifecycle/bundled-digests.js +4 -3
  42. package/dist/lifecycle/install.d.ts +7 -0
  43. package/dist/lifecycle/install.js +45 -3
  44. package/dist/lifecycle/manifest.d.ts +2 -2
  45. package/dist/lifecycle/manifest.js +5 -3
  46. package/dist/lifecycle/node.js +2 -0
  47. package/dist/lifecycle/persistent-state.js +1 -1
  48. package/dist/lifecycle/ports.d.ts +8 -0
  49. package/dist/lifecycle/seed-bundled.js +4 -1
  50. package/dist/manifest.d.ts +1 -1
  51. package/dist/manifest.js +3 -2
  52. package/fly/deploy-configs/fly.mjs +115 -0
  53. package/fly/deploy-ops/fly.mjs +102 -0
  54. package/fly/mcp.json +4 -0
  55. package/fly/plugin.json +81 -0
  56. package/fly/scripts/render-deploy-workflow.mjs +150 -0
  57. package/fly/skills/fly/SKILL.md +43 -0
  58. package/fly/skills/fly/references/fly-deploy.template.yml +117 -0
  59. package/fly/skills/fly/references/fly.template.toml +81 -0
  60. package/fly/tovu-credential-schemes.json +11 -0
  61. package/fly/tovu-deploy-configs.json +21 -0
  62. package/fly/tovu-deploy-ops.json +14 -0
  63. package/github/deploy-ops/github-actions.mjs +164 -0
  64. package/github/deployments/github.mjs +415 -0
  65. package/github/mcp.json +4 -0
  66. package/github/plugin.json +105 -0
  67. package/github/skills/github/SKILL.md +249 -0
  68. package/github/skills/github/references/actions.md +224 -0
  69. package/github/skills/github/references/auth-and-tokens.md +140 -0
  70. package/github/skills/github/references/repo-files.md +135 -0
  71. package/github/skills/github/references/site-backup.md +159 -0
  72. package/github/skills/github/references/source-control.md +123 -0
  73. package/github/source-control/backup-push.mjs +242 -0
  74. package/github/source-control/commit-site.mjs +459 -0
  75. package/github/source-control/github.mjs +93 -0
  76. package/github/source-control/write-files.mjs +468 -0
  77. package/github/tovu-deploy-ops.json +13 -0
  78. package/github/tovu-source-control.json +117 -0
  79. package/github-pages/NOTES.md +19 -0
  80. package/github-pages/mcp.json +4 -0
  81. package/github-pages/plugin.json +48 -0
  82. package/github-pages/skills/github-pages/SKILL.md +44 -0
  83. package/github-pages/targets/github-pages.mjs +543 -0
  84. package/github-pages/tovu-deploy-targets.json +327 -0
  85. package/google-login/exports/google-g.svg +1 -0
  86. package/google-login/exports/sign-in-button.css +68 -0
  87. package/google-login/exports/sign-in-button.html +4 -0
  88. package/google-login/mcp.json +4 -0
  89. package/google-login/plugin.json +59 -0
  90. package/google-login/skills/google-login/SKILL.md +96 -0
  91. package/google-login/tovu-login-providers.json +21 -0
  92. package/google-workspace/README.md +132 -0
  93. package/google-workspace/form-destinations/README.md +87 -0
  94. package/google-workspace/form-destinations/dedupe.mjs +43 -0
  95. package/google-workspace/form-destinations/delivery-result.mjs +69 -0
  96. package/google-workspace/form-destinations/mapping.mjs +152 -0
  97. package/google-workspace/form-destinations/sheets.mjs +202 -0
  98. package/google-workspace/lib/auth.mjs +81 -0
  99. package/google-workspace/lib/drive-files.mjs +54 -0
  100. package/google-workspace/lib/form-destination-settings.mjs +72 -0
  101. package/google-workspace/lib/http.mjs +168 -0
  102. package/google-workspace/lib/picker-flow.mjs +171 -0
  103. package/google-workspace/lib/picker-selection.mjs +107 -0
  104. package/google-workspace/lib/product-state.mjs +93 -0
  105. package/google-workspace/lib/request-policy.mjs +163 -0
  106. package/google-workspace/lib/sheets-a1.mjs +79 -0
  107. package/google-workspace/lib/sheets-admin-csv.mjs +82 -0
  108. package/google-workspace/lib/sheets-admin-request-kinds.mjs +183 -0
  109. package/google-workspace/lib/sheets-client.mjs +286 -0
  110. package/google-workspace/lib/surface-actions.mjs +107 -0
  111. package/google-workspace/mcp.json +4 -0
  112. package/google-workspace/plugin.json +77 -0
  113. package/google-workspace/products.json +12 -0
  114. package/google-workspace/skills/google-workspace/SKILL.md +82 -0
  115. package/google-workspace/skills/google-workspace/references/connection.md +102 -0
  116. package/google-workspace/skills/google-workspace/references/form-delivery.md +50 -0
  117. package/google-workspace/skills/google-workspace/references/sheets.md +149 -0
  118. package/google-workspace/skills/google-workspace/references/troubleshooting.md +31 -0
  119. package/google-workspace/tools/account-info.mjs +18 -0
  120. package/google-workspace/tools/sheets/README.md +54 -0
  121. package/google-workspace/tools/sheets/copy-tab.mjs +23 -0
  122. package/google-workspace/tools/sheets/copy.mjs +23 -0
  123. package/google-workspace/tools/sheets/create.mjs +35 -0
  124. package/google-workspace/tools/sheets/find-replace.mjs +38 -0
  125. package/google-workspace/tools/sheets/find.mjs +52 -0
  126. package/google-workspace/tools/sheets/get-by-data-filter.mjs +23 -0
  127. package/google-workspace/tools/sheets/get.mjs +22 -0
  128. package/google-workspace/tools/sheets/values-append.mjs +27 -0
  129. package/google-workspace/tools/sheets/values-batch-clear.mjs +20 -0
  130. package/google-workspace/tools/sheets/values-batch-get.mjs +20 -0
  131. package/google-workspace/tools/sheets/values-batch-update.mjs +29 -0
  132. package/google-workspace/tools/sheets/values-by-data-filter.mjs +62 -0
  133. package/google-workspace/tools/sheets/values-clear.mjs +20 -0
  134. package/google-workspace/tools/sheets/values-get.mjs +21 -0
  135. package/google-workspace/tools/sheets/values-update.mjs +26 -0
  136. package/google-workspace/tools/sheets-admin/README.md +45 -0
  137. package/google-workspace/tools/sheets-admin/basic-filter.mjs +32 -0
  138. package/google-workspace/tools/sheets-admin/batch-update.mjs +25 -0
  139. package/google-workspace/tools/sheets-admin/developer-metadata.mjs +28 -0
  140. package/google-workspace/tools/sheets-admin/export-csv.mjs +67 -0
  141. package/google-workspace/tools/sheets-admin/filter-views.mjs +38 -0
  142. package/google-workspace/tools/sheets-admin/format.mjs +70 -0
  143. package/google-workspace/tools/sheets-admin/named-ranges.mjs +42 -0
  144. package/google-workspace/tools/sheets-admin/open-existing.mjs +34 -0
  145. package/google-workspace/tools/sheets-admin/protected-ranges-get.mjs +18 -0
  146. package/google-workspace/tools/sheets-admin/sort.mjs +18 -0
  147. package/google-workspace/tools/sheets-admin/tab-modify.mjs +36 -0
  148. package/google-workspace/tovu-admin-surfaces.json +63 -0
  149. package/google-workspace/tovu-agent-tools.json +1178 -0
  150. package/google-workspace/tovu-connections.json +35 -0
  151. package/google-workspace/tovu-form-destinations.json +10 -0
  152. package/google-workspace/ui/README.md +73 -0
  153. package/google-workspace/ui/connection-panel.handler.mjs +167 -0
  154. package/google-workspace/ui/connection-panel.html +17 -0
  155. package/google-workspace/ui/connection-panel.mjs +189 -0
  156. package/google-workspace/ui/connection-view.mjs +120 -0
  157. package/google-workspace/ui/destination-panel.handler.mjs +106 -0
  158. package/google-workspace/ui/destination-panel.html +17 -0
  159. package/google-workspace/ui/destination-panel.mjs +150 -0
  160. package/google-workspace/ui/dom.mjs +140 -0
  161. package/google-workspace/ui/panel.css +170 -0
  162. package/google-workspace/ui/picker-flow.mjs +87 -0
  163. package/google-workspace/ui/picker-view.mjs +44 -0
  164. package/google-workspace/ui/picker.handler.mjs +56 -0
  165. package/google-workspace/ui/picker.html +17 -0
  166. package/google-workspace/ui/picker.mjs +88 -0
  167. package/google-workspace/ui/surface-client.mjs +227 -0
  168. package/higgsfield-media/mcp.json +9 -0
  169. package/higgsfield-media/plugin.json +109 -0
  170. package/higgsfield-media/skills/higgsfield-media/SKILL.md +325 -0
  171. package/higgsfield-media/skills/higgsfield-media/references/failure-modes.md +116 -0
  172. package/higgsfield-media/skills/higgsfield-media/references/models-and-plan-gates.md +142 -0
  173. package/jev/mcp.json +4 -0
  174. package/jev/plugin.json +44 -0
  175. package/jev/skills/jev/SKILL.md +308 -0
  176. package/jev/skills/jev/references/cookbooks.md +197 -0
  177. package/jev/skills/jev/references/jaggedness.md +135 -0
  178. package/jev/skills/jev/references/javascript-sdk.md +146 -0
  179. package/jev/skills/jev/references/patterns.md +123 -0
  180. package/jev/skills/jev/references/primitives-reference.md +187 -0
  181. package/langfuse/plugin.json +22 -0
  182. package/meilisearch/mcp.json +4 -0
  183. package/meilisearch/plugin.json +54 -0
  184. package/meilisearch/providers/meilisearch.mjs +651 -0
  185. package/meilisearch/skills/meilisearch/SKILL.md +43 -0
  186. package/meilisearch/tovu-search-providers.json +10 -0
  187. package/netlify/NOTES.md +13 -0
  188. package/netlify/mcp.json +4 -0
  189. package/netlify/plugin.json +47 -0
  190. package/netlify/skills/netlify/SKILL.md +39 -0
  191. package/netlify/targets/netlify.mjs +384 -0
  192. package/netlify/tovu-consent.json +6 -0
  193. package/netlify/tovu-deploy-targets.json +163 -0
  194. package/opentelemetry/mcp.json +4 -0
  195. package/opentelemetry/plugin.json +52 -0
  196. package/opentelemetry/skills/opentelemetry/SKILL.md +39 -0
  197. package/package.json +161 -6
  198. package/posthog/mcp.json +4 -0
  199. package/posthog/plugin.json +46 -0
  200. package/posthog/skills/posthog/SKILL.md +36 -0
  201. package/railway/deploy-configs/railway.mjs +135 -0
  202. package/railway/deploy-ops/railway.mjs +167 -0
  203. package/railway/mcp.json +4 -0
  204. package/railway/plugin.json +45 -0
  205. package/railway/skills/railway/SKILL.md +66 -0
  206. package/railway/tovu-deploy-configs.json +16 -0
  207. package/railway/tovu-deploy-ops.json +11 -0
  208. package/render/deploy-configs/render.mjs +141 -0
  209. package/render/deploy-ops/render.mjs +152 -0
  210. package/render/mcp.json +4 -0
  211. package/render/plugin.json +47 -0
  212. package/render/skills/render/SKILL.md +63 -0
  213. package/render/tovu-deploy-configs.json +16 -0
  214. package/render/tovu-deploy-ops.json +11 -0
  215. package/resend/NOTES.md +52 -0
  216. package/resend/adapters/resend.mjs +161 -0
  217. package/resend/lib/domains.mjs +137 -0
  218. package/resend/lib/errors.mjs +25 -0
  219. package/resend/lib/feedback.mjs +118 -0
  220. package/resend/mcp.json +13 -0
  221. package/resend/plugin.json +192 -0
  222. package/resend/skills/resend/SKILL.md +107 -0
  223. package/resend/tovu-mail-adapters.json +22 -0
  224. package/s3-compatible/NOTES.md +11 -0
  225. package/s3-compatible/mcp.json +4 -0
  226. package/s3-compatible/object-stores/s3-compatible.mjs +287 -0
  227. package/s3-compatible/plugin.json +56 -0
  228. package/s3-compatible/skills/s3-compatible/SKILL.md +39 -0
  229. package/s3-compatible/targets/s3-compatible.mjs +1054 -0
  230. package/s3-compatible/tovu-deploy-targets.json +432 -0
  231. package/s3-compatible/tovu-object-stores.json +29 -0
  232. package/sentry/mcp.json +4 -0
  233. package/sentry/plugin.json +49 -0
  234. package/sentry/skills/sentry/SKILL.md +34 -0
  235. package/shopify/mcp.json +4 -0
  236. package/shopify/plugin.json +22 -0
  237. package/shopify/skills/shopify/SKILL.md +103 -0
  238. package/shopify/skills/shopify/references/catalog-sync.md +74 -0
  239. package/shopify/skills/shopify/references/domains.md +26 -0
  240. package/shopify/skills/shopify/references/live-storefront.md +34 -0
  241. package/shopify/skills/shopify/references/storefront-snippets.md +21 -0
  242. package/site-compliance/mcp.json +4 -0
  243. package/site-compliance/plugin.json +34 -0
  244. package/site-compliance/skills/site-compliance/SKILL.md +220 -0
  245. package/site-compliance/skills/site-compliance/references/ccpa-cpra.md +115 -0
  246. package/site-compliance/skills/site-compliance/references/gdpr-eu-uk.md +143 -0
  247. package/site-compliance/skills/site-compliance/references/wcag-2-2.md +129 -0
  248. package/site-import/mcp.json +4 -0
  249. package/site-import/plugin.json +41 -0
  250. package/site-import/skills/site-import/SKILL.md +232 -0
  251. package/site-import/skills/site-import/references/content-mapping.md +166 -0
  252. package/site-import/skills/site-import/references/discovery-and-classification.md +117 -0
  253. package/site-import/skills/site-import/references/theme-extraction.md +34 -0
  254. package/slide-design/com.anthropic.claude-code/hooks/hooks.json +4 -0
  255. package/slide-design/mcp.json +4 -0
  256. package/slide-design/plugin.json +26 -0
  257. package/slide-design/skills/slide-design/SKILL.md +103 -0
  258. package/slide-design/skills/slide-design/references/charts-and-exhibits.md +100 -0
  259. package/slide-design/skills/slide-design/references/html-deck.md +175 -0
  260. package/slide-design/skills/slide-design/references/layout-and-style.md +131 -0
  261. package/slide-design/skills/slide-design/references/review.md +93 -0
  262. package/slide-design/skills/slide-design/references/storyline.md +118 -0
  263. package/stripe/plugin.json +22 -0
  264. package/supabase/mcp.json +26 -0
  265. package/supabase/plugin.json +78 -0
  266. package/supabase/skills/supabase/SKILL.md +169 -0
  267. package/supabase/skills/supabase/references/failure-modes.md +101 -0
  268. package/ui-ux-design/CHANGELOG.md +28 -0
  269. package/ui-ux-design/LICENSE +201 -0
  270. package/ui-ux-design/NOTICE +14 -0
  271. package/ui-ux-design/README.md +62 -0
  272. package/ui-ux-design/com.anthropic.claude-code/hooks/hooks.json +4 -0
  273. package/ui-ux-design/mcp.json +4 -0
  274. package/ui-ux-design/plugin.json +26 -0
  275. package/ui-ux-design/skills/frontend-accessibility/SKILL.md +60 -0
  276. package/ui-ux-design/skills/gstack-design/SKILL.md +53 -0
  277. package/ui-ux-design/skills/gstack-design/references/consultation.md +30 -0
  278. package/ui-ux-design/skills/gstack-design/references/html.md +29 -0
  279. package/ui-ux-design/skills/gstack-design/references/review.md +28 -0
  280. package/ui-ux-design/skills/gstack-design/references/shotgun.md +33 -0
  281. package/ui-ux-design/skills/gstack-design/references/upstream-notes.md +23 -0
  282. package/ui-ux-design/skills/interface-design/ORIGINAL.md +16 -0
  283. package/ui-ux-design/skills/interface-design/SKILL.md +41 -0
  284. package/ui-ux-design/skills/interface-design/references/commands/audit.md +40 -0
  285. package/ui-ux-design/skills/interface-design/references/commands/critique.md +21 -0
  286. package/ui-ux-design/skills/interface-design/references/commands/extract.md +34 -0
  287. package/ui-ux-design/skills/interface-design/references/commands/init.md +31 -0
  288. package/ui-ux-design/skills/interface-design/references/commands/status.md +22 -0
  289. package/ui-ux-design/skills/interface-design/references/examples/system-precision.md +29 -0
  290. package/ui-ux-design/skills/interface-design/references/examples/system-warmth.md +29 -0
  291. package/ui-ux-design/skills/interface-design/references/system-template.md +57 -0
  292. package/ui-ux-design/skills/shadcn-ui/README.md +244 -0
  293. package/ui-ux-design/skills/shadcn-ui/SKILL.md +333 -0
  294. package/ui-ux-design/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
  295. package/ui-ux-design/skills/shadcn-ui/examples/data-table.tsx +313 -0
  296. package/ui-ux-design/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
  297. package/ui-ux-design/skills/shadcn-ui/resources/component-catalog.md +481 -0
  298. package/ui-ux-design/skills/shadcn-ui/resources/customization-guide.md +516 -0
  299. package/ui-ux-design/skills/shadcn-ui/resources/migration-guide.md +463 -0
  300. package/ui-ux-design/skills/shadcn-ui/resources/setup-guide.md +412 -0
  301. package/ui-ux-design/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
  302. package/ui-ux-design/skills/ui-ux-design/SKILL.md +171 -0
  303. package/ui-ux-design/skills/ui-ux-design/references/brand-and-voice.md +78 -0
  304. package/ui-ux-design/skills/ui-ux-design/references/chrome-mobile-test-matrix.md +137 -0
  305. package/ui-ux-design/skills/ui-ux-design/references/components-and-states.md +112 -0
  306. package/ui-ux-design/skills/ui-ux-design/references/delight-and-motion.md +54 -0
  307. package/ui-ux-design/skills/ui-ux-design/references/foundations.md +145 -0
  308. package/ui-ux-design/skills/ui-ux-design/references/inclusive-ai-imagery.md +57 -0
  309. package/ui-ux-design/skills/ui-ux-design/references/kole-jain-uiux-concepts.md +104 -0
  310. package/ui-ux-design/skills/ui-ux-design/references/kuinetic-reference.md +184 -0
  311. package/ui-ux-design/skills/ui-ux-design/references/openai-frontend-skill.md +139 -0
  312. package/ui-ux-design/skills/ui-ux-design/references/premium-ui.md +55 -0
  313. package/ui-ux-design/skills/ui-ux-design/references/research-and-validation.md +88 -0
  314. package/ui-ux-design/skills/ui-ux-design/references/sam-crawford-premium-websites.md +103 -0
  315. package/ui-ux-design/skills/ui-ux-design/references/self-made-web-designer-core-skills.md +119 -0
  316. package/ui-ux-design/skills/ui-ux-design/references/ui-animation.md +173 -0
  317. package/ui-ux-design/skills/ui-ux-design/references/ui-loop.md +234 -0
  318. package/ui-ux-design/skills/ui-ux-design/references/visual-storytelling.md +54 -0
  319. package/ui-ux-design/skills/vercel-web-design-guidelines/SKILL.md +39 -0
  320. package/ui-ux-design/skills/web-compliance/SKILL.md +134 -0
  321. package/ui-ux-design/skills/web-compliance/references/audit-evidence.md +59 -0
  322. package/vercel/NOTES.md +23 -0
  323. package/vercel/mcp.json +4 -0
  324. package/vercel/object-stores/vercel-blob.mjs +147 -0
  325. package/vercel/plugin.json +47 -0
  326. package/vercel/skills/vercel/SKILL.md +48 -0
  327. package/vercel/targets/vercel.mjs +586 -0
  328. package/vercel/tovu-consent.json +6 -0
  329. package/vercel/tovu-deploy-targets.json +219 -0
  330. package/vercel/tovu-object-stores.json +10 -0
  331. package/wordpress/NOTES.md +33 -0
  332. package/wordpress/plugin.json +30 -0
  333. package/wordpress/skills/wordpress/SKILL.md +43 -0
  334. package/wordpress/skills/wordpress/references/free-site-redirect.md +46 -0
  335. package/wordpress/skills/wordpress/references/subdomain.md +66 -0
@@ -0,0 +1,48 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "github-pages",
4
+ "version": "0.1.0",
5
+ "description": "Publishes the site's static export to GitHub Pages with the workspace's own saved token. Ships the host code itself (a host loads it only because that host chose to ship this plugin) and the know-how an agent needs: which token permissions to grant, never guessing the owner, the address and base path, and what each failure means in plain words.",
6
+ "author": "Tovu",
7
+ "license": "Apache-2.0",
8
+ "extensions": {
9
+ "tovu": {
10
+ "vendorAliases": ["github_pages", "github pages", "githubpages", "github.io"],
11
+ "displayName": "GitHub Pages",
12
+ "summary": "Puts your website online on GitHub Pages. The assistant shows you a preview first.",
13
+ "id": "tovu/github-pages",
14
+ "provides": [
15
+ "deploy-target:github-pages"
16
+ ],
17
+ "credentialCards": [
18
+ {
19
+ "target": "github-pages",
20
+ "title": "Connect GitHub Pages",
21
+ "label": "GitHub Pages connection",
22
+ "category": "hosting",
23
+ "intro": "Connect GitHub Pages to commit this site’s static export and enable Pages for the selected repository.",
24
+ "usedFor": [
25
+ "Publishes your website to the selected repository's GitHub Pages site"
26
+ ],
27
+ "keyHint": "Select only the destination repository. Fine-grained token: Contents and Pages → Read and write. A classic token needs repo scope.",
28
+ "saveKind": "publish-host",
29
+ "helpUrl": "https://github.com/settings/personal-access-tokens/new",
30
+ "helpLabel": "Create a token",
31
+ "baseUrl": "https://api.github.com"
32
+ }
33
+ ]
34
+ }
35
+ },
36
+ "keywords": [
37
+ "github-pages",
38
+ "github",
39
+ "deploy",
40
+ "publish",
41
+ "hosting",
42
+ "static-site",
43
+ "static-export",
44
+ "go-live",
45
+ "website",
46
+ "deploy-target:github-pages"
47
+ ]
48
+ }
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: github-pages
3
+ description: Publish the site's static export to GitHub Pages with the workspace's own saved token. Covers the token, the owner, repo and branch fields, the address and base path, and what each failure means in plain words.
4
+ ---
5
+
6
+ # GitHub Pages
7
+
8
+ ## Connection card
9
+
10
+ Choose the card yourself from its `usedFor` list. Reuse a saved key or working sign-in for that
11
+ task. Never offer a replacement unless the person asks.
12
+
13
+ Open the plugin’s **Connect GitHub Pages** card with `credential_save {kind:"plugin-provider",target:"github-pages"}`. Select only the destination repository and grant Contents and Pages **Read and write**. The card links to token creation; the hosting owner verifies and seals it.
14
+
15
+ The steps are the same for every host: `deployment_get_static_publish_capabilities` shows whether a
16
+ credential is saved, `deployment_preview_static_publish` comes first, and `deployment_execute_static_publish`
17
+ publishes once the person confirms. `ok: "partial"` means not reachable yet: never call it failed or live.
18
+ This file is what is particular to GitHub Pages.
19
+
20
+ - **Token:** a fine-grained personal access token (https://github.com/settings/personal-access-tokens)
21
+ for the repository, with **Contents: Read and write** and **Pages: Read and write**. Save it with the plugin card above.
22
+ - **Config fields:** `owner` (user or organization), `repo`, and optional `branch` (default `gh-pages`).
23
+ The host commits the site to that branch and switches GitHub Pages on for it if it is off.
24
+ - **Owner:** never guess it from the person's name or email address. Use the verified token's login
25
+ (`accountLabel` in the capabilities result) and confirm it with the person. If `accountLabel` is null,
26
+ say the account is not known and ask for the GitHub user or organization. Do not offer an example
27
+ name: an invented one was once read back as a real suggestion and published to a repo nobody owned.
28
+ - **Address:** `https://<owner>.github.io/<repo>/`. The site is built with every link under `/<repo>`,
29
+ so previewing shows that base path.
30
+ - **`.nojekyll`:** this plugin adds it, so GitHub does not run Jekyll and drop files whose names start
31
+ with `_`.
32
+ - **Security headers:** GitHub Pages cannot set custom response headers, so none are sent.
33
+
34
+ ## Failure messages and what to tell the person
35
+
36
+ | Message | Meaning | Next step |
37
+ |---|---|---|
38
+ | `GitHub token is required.` | No token in the saved credential. | Open the plugin card above. |
39
+ | `invalid GitHub owner '...'` / `invalid GitHub repo '...'` / `invalid branch name '...'` | A config field has a typo or bad characters. | Correct the owner, repo, or branch name. |
40
+ | `Bad credentials` / `Resource not accessible by personal access token` (401/403) | The token is wrong, expired, or lacks the permissions above. | Check this repo's Contents and Pages access; replace the token only if asked. |
41
+ | `Not Found` (404) | The repo does not exist, or the token cannot see it. | Check the owner and repo names and the token's repository access. |
42
+ | `GitHub Pages build errored.` | GitHub accepted the files but its Pages build failed. | Retry once; if it repeats, check the repo's Pages settings. |
43
+ | `... attempted to redirect an authenticated request ...` | Something redirected the call; the host refused to send the token on. | Retry later; if it repeats, check the server's network or proxy. |
44
+ | `GitHub returned a non-JSON response.` / `fetch timed out after ...` | GitHub or the network had a hiccup. | Retry in a minute. |
@@ -0,0 +1,543 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ /**
4
+ * @file GitHub Pages deploy target of the `github-pages` Agent Plugin.
5
+ *
6
+ * Ported from `@jini-ai/devops` `src/deploy/github-pages.ts` at Jini commit 28f67f9a: the body of
7
+ * {@link bindGitHubPages} is that source with its TypeScript types stripped, same GitHub calls, error
8
+ * texts and redirect guard. Network, timeouts, poll delay, reachability, the redirect guard and
9
+ * `DeployError` come from the host kit the host injects (see {@link bindGitHubPages}), bound once
10
+ * per target: an installed plugin has no npm dependencies.
11
+ *
12
+ * What the first host (Tovu) used to add in its core around this target now lives in the module
13
+ * contract at the bottom: the `.nojekyll` marker, the `/<repo>` base path, and the owner/repo/branch
14
+ * validation. GitHub Pages has no response-header config, so `responseHeaders` is ignored.
15
+ *
16
+ * Plain JS by owner decision (2026-09-29).
17
+ */
18
+
19
+ export const GITHUB_PAGES_TARGET_ID = "github-pages";
20
+
21
+ /** GitHub Pages runs Jekyll over published content unless this empty root file exists, and Jekyll
22
+ * silently drops every path segment starting with `_`. */
23
+ const NOJEKYLL_FILE = ".nojekyll";
24
+
25
+ /** GitHub owner/org: alphanumeric plus single hyphens, not starting with one, at most 39 characters. */
26
+ const OWNER_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9-]{0,38})$/;
27
+ /** GitHub repo name: letters, digits, `.`/`-`/`_`, at most 100 characters. */
28
+ const REPO_PATTERN = /^[A-Za-z0-9_.-]{1,100}$/;
29
+ /** Git branch name: deliberately permissive, but no whitespace or control characters. */
30
+ const BRANCH_PATTERN = /^[A-Za-z0-9._/-]{1,250}$/;
31
+
32
+ /**
33
+ * The file set actually committed: `files` plus the `.nojekyll` marker, unless the site ships one.
34
+ *
35
+ * @param {Array<{ file: string, data: string | Uint8Array, contentType?: string }>} files
36
+ */
37
+ function withNoJekyll(files) {
38
+ return files.some((file) => file.file === NOJEKYLL_FILE) ? files : [...files, { file: NOJEKYLL_FILE, data: "" }];
39
+ }
40
+
41
+ /**
42
+ * Binds the ported GitHub Pages code to one host kit.
43
+ *
44
+ * @param {{ fetch(url: string, init: RequestInit, options: { timeoutMs: number }): Promise<Response>,
45
+ * timeouts: { QUICK: number, DEPLOY: number, UPLOAD: number }, sleep(ms: number): Promise<void> } & Record<string, any>} kit
46
+ * The host's deploy kit in the positional ABI installed modules are written against (devops'
47
+ * `DeployHostKit` helpers, called positionally; `DeployError` is `new (message, status?, details?)`).
48
+ * @returns The target class, constructed with `{ token, owner, repo, branch? }` as in devops.
49
+ */
50
+ export function bindGitHubPages(kit) {
51
+ const { fetch: fetchWithTimeout, timeouts: FETCH_TIMEOUT_MS, sleep, DeployError, checkDeploymentUrl, normalizeDeploymentUrl, waitForReachableDeploymentUrl, assertNotRedirected, redirectGuardInit } = kit;
52
+ const GITHUB_PAGES_TARGET_ID = 'github-pages';
53
+ const GITHUB_API = 'https://api.github.com';
54
+ /** Pinned per GitHub's own recommendation (`X-GitHub-Api-Version` header) so a future server-side default bump can't silently change this file's response shapes underneath it. */
55
+ const GITHUB_API_VERSION = '2026-03-10';
56
+ /** The long-standing GitHub Pages convention branch name (used by `gh-pages`-the-npm-package, `JamesIves/github-pages-deploy-action`, etc.) — deliberately distinct from a repo's default/production branch so a publish never touches source code. */
57
+ const DEFAULT_GITHUB_PAGES_BRANCH = 'gh-pages';
58
+ /**
59
+ * The only two terminal values this file commits to, independently
60
+ * cross-validated against `GET /repos/{owner}/{repo}/pages`'s own confirmed
61
+ * clean enum (`built | building | errored | null` — documented as "the
62
+ * status of the most recent build", the same underlying concept
63
+ * `builds/latest`'s `status` field represents). Any other value —
64
+ * `'building'`, `null`, or an unlisted future value — is treated as
65
+ * in-progress; see `pollGitHubPagesBuild`'s doc comment for why this file
66
+ * does not commit to `builds/latest`'s full enum (GitHub's own docs render
67
+ * that field's enum widget client-side and it did not come through cleanly
68
+ * via a direct fetch of the docs page during this file's research pass).
69
+ */
70
+ const GITHUB_PAGES_BUILD_SUCCESS = 'built';
71
+ const GITHUB_PAGES_BUILD_FAILURE = 'errored';
72
+ /**
73
+ * `DeployTarget` adapter for GitHub Pages, built on the Git Data API (create
74
+ * blobs → a tree → a commit → create/update a branch ref) rather than the
75
+ * newer artifact-based "Pages deployments" API
76
+ * (`POST /repos/{owner}/{repo}/pages/deployments`). See
77
+ * `packages/devops/source-map.md`'s 2026-07-21 addition for the full
78
+ * research trail; in short: that endpoint requires an `oidc_token` "issued
79
+ * by GitHub Actions certifying the origin of the deployment" — a token this
80
+ * package (running outside an Actions runner, authenticating with a plain
81
+ * PAT) has no way to mint. The Git Data API has no such requirement: any
82
+ * token with repo write access can push a commit and (once the branch
83
+ * exists) enable/observe Pages against it, which is exactly what
84
+ * `actions/deploy-pages`'s *predecessor* tooling (and every non-Actions
85
+ * GitHub Pages deploy tool in the wild, e.g. `gh-pages`/
86
+ * `JamesIves/github-pages-deploy-action` in non-artifact mode) has always
87
+ * done.
88
+ *
89
+ * Unlike `VercelDeployTarget`/`NetlifyDeployTarget`/`CloudflarePagesDeployTarget`,
90
+ * this target does not derive a project/site identity from
91
+ * `input.projectName` — a GitHub Pages "site" is intrinsically the
92
+ * `{owner, repo}` the caller configures, not a freely-named resource this
93
+ * package could create on the fly. `input.projectName` is used only as a
94
+ * human label in the generated commit message.
95
+ */
96
+ class GitHubPagesDeployTarget {
97
+ config;
98
+ id = GITHUB_PAGES_TARGET_ID;
99
+ constructor(config) {
100
+ this.config = config;
101
+ }
102
+ /** Existing Pages sites can use a custom domain; read it before rendering. A new site has
103
+ * GitHub's fixed owner/repository address until its first Pages branch exists.
104
+ * @complexity One bounded read, no branch writes or Pages provisioning.
105
+ */
106
+ async resolvePublicUrl(_required, _options = {}) {
107
+ const { owner, repo } = this.config;
108
+ const response = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/pages`, redirectGuardInit({ headers: githubHeaders(this.config.token) }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
109
+ assertNotRedirected(response, 'GitHub Pages public URL lookup');
110
+ const body = await readGitHubJson(response);
111
+ if (response.status === 404) return `https://${owner.toLowerCase()}.github.io${repo.toLowerCase() === `${owner.toLowerCase()}.github.io` ? '' : `/${repo}`}`;
112
+ if (!response.ok) throw githubError(body, response.status, 'GitHub Pages site lookup failed.');
113
+ const url = githubPagesUrlCandidates(body)[0];
114
+ if (!url) throw new DeployError('GitHub Pages did not report a public URL.', 502);
115
+ return url;
116
+ }
117
+ /**
118
+ * Publishes `input.files` as a new commit on the configured Pages branch
119
+ * (creating the branch if it doesn't exist yet), ensures the repository's
120
+ * Pages site is enabled against that branch, waits for GitHub's own build
121
+ * of that exact commit to finish, then waits for the resulting URL to
122
+ * become publicly reachable.
123
+ *
124
+ * Sequencing note: the branch is pushed to *before* Pages is
125
+ * enabled/verified, not after. `POST /repos/{owner}/{repo}/pages` requires
126
+ * `source.branch` to already exist in the repository — enabling Pages
127
+ * against a branch that doesn't exist yet fails, so the branch must be
128
+ * created first via the Git Data API flow below.
129
+ *
130
+ * @param input - File set plus a caller-chosen label used only for the commit message.
131
+ * @throws {DeployError} If `config.token`/`config.owner`/`config.repo` is
132
+ * missing, or GitHub's API rejects any step of the blob/tree/commit/ref/
133
+ * pages flow, or the resulting Pages build reaches `status: 'errored'`.
134
+ */
135
+ async publish(input) {
136
+ if (!this.config.token) {
137
+ throw new DeployError('GitHub token is required.', 400);
138
+ }
139
+ const { owner, repo } = this.config;
140
+ if (!owner || !repo) {
141
+ throw new DeployError('GitHub Pages owner and repo are required.', 400);
142
+ }
143
+ const branch = this.config.branch || DEFAULT_GITHUB_PAGES_BRANCH;
144
+ const { parentSha, commitSha } = await pushGitHubPagesFiles({ config: this.config, owner, repo, branch, input });
145
+ const { site, sourceBranchMismatch } = await prepareGitHubPagesSite({ config: this.config, owner, repo, branch, commitSha });
146
+ const candidates = githubPagesUrlCandidates(site);
147
+ const link = await waitForReachableDeploymentUrl(candidates, { providerLabel: 'GitHub Pages' });
148
+ return {
149
+ targetId: this.id,
150
+ url: link.url || candidates[0] || '',
151
+ deploymentId: commitSha,
152
+ status: link.status,
153
+ statusMessage: link.statusMessage,
154
+ ...(link.reachableAt !== undefined ? { reachableAt: link.reachableAt } : {}),
155
+ providerMetadata: {
156
+ owner,
157
+ repo,
158
+ branch,
159
+ commitSha,
160
+ branchCreated: !parentSha,
161
+ ...(sourceBranchMismatch ? { sourceBranchMismatch: true } : {}),
162
+ },
163
+ };
164
+ }
165
+ /** Probes `url` with the shared reachability checker — no documented GitHub Pages equivalent to Vercel's Deployment Protection auth wall was found, same reasoning `netlify.ts`/`cloudflare-pages.ts` already applied to their own `checkReachability`. */
166
+ async checkReachability(url) {
167
+ return checkDeploymentUrl(url);
168
+ }
169
+ }
170
+ async function pushGitHubPagesFiles({ config, owner, repo, branch, input }, _options = {}) {
171
+ const parentSha = await getGitHubRefSha(config, owner, repo, branch);
172
+ const treeSha = await createGitHubTreeFromFiles(config, owner, repo, input.files);
173
+ const label = typeof input.projectName === 'string' && input.projectName.trim() ? input.projectName.trim() : 'site';
174
+ const commitSha = await createGitHubCommit(config, owner, repo, `Deploy ${label} via @jini-ai/deploy`, treeSha, parentSha ? [parentSha] : []);
175
+ if (parentSha) {
176
+ await updateGitHubRef(config, owner, repo, branch, commitSha);
177
+ }
178
+ else {
179
+ await createGitHubRef(config, owner, repo, branch, commitSha);
180
+ }
181
+ return { parentSha, commitSha };
182
+ }
183
+ async function prepareGitHubPagesSite({ config, owner, repo, branch, commitSha }, _options = {}) {
184
+ const site = await ensureGitHubPagesSite(config, owner, repo, branch);
185
+ const configuredBranch = typeof site.source?.branch === 'string' ? site.source.branch : undefined;
186
+ const sourceBranchMismatch = configuredBranch !== undefined && configuredBranch !== branch;
187
+ const build = await pollGitHubPagesBuild(config, owner, repo, commitSha);
188
+ if (build?.status === GITHUB_PAGES_BUILD_FAILURE) {
189
+ throw new DeployError(build.errorMessage || 'GitHub Pages build errored.', 502, build.raw);
190
+ }
191
+ return { site, sourceBranchMismatch };
192
+ }
193
+ function enc(value) {
194
+ return encodeURIComponent(value);
195
+ }
196
+ function githubHeaders(token, extra = {}) {
197
+ return {
198
+ Authorization: `Bearer ${token}`,
199
+ Accept: 'application/vnd.github+json',
200
+ 'X-GitHub-Api-Version': GITHUB_API_VERSION,
201
+ ...extra,
202
+ };
203
+ }
204
+ async function readGitHubJson(resp) {
205
+ try {
206
+ return (await resp.json());
207
+ }
208
+ catch {
209
+ throw new DeployError('GitHub returned a non-JSON response.', resp.status || 502);
210
+ }
211
+ }
212
+ /**
213
+ * `fallback` (the second operand of the `||`) is unreachable today: every
214
+ * one of this file's 8 call sites passes a non-empty string literal — same
215
+ * situation `netlify.ts`'s `netlifyError` documents for its own callers.
216
+ * Kept as the sane default a future caller forgetting to pass `fallback`
217
+ * would fall through to, not deleted for coverage's sake.
218
+ */
219
+ function githubError(json, status, fallback) {
220
+ const message = typeof json?.message === 'string' && json.message ? json.message : fallback || `GitHub request failed (${status}).`;
221
+ return new DeployError(message, status, json);
222
+ }
223
+ function sha256Hex(data) {
224
+ return createHash('sha256').update(Buffer.from(data)).digest('hex');
225
+ }
226
+ /** Looks up the tip commit sha of `heads/{branch}`, or `undefined` if the branch does not exist yet (a brand-new Pages publish). */
227
+ async function getGitHubRefSha(config, owner, repo, branch) {
228
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/ref/heads/${enc(branch)}`, redirectGuardInit({ headers: githubHeaders(config.token) }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
229
+ assertNotRedirected(resp, 'GitHub Pages branch lookup');
230
+ if (resp.status === 404)
231
+ return undefined;
232
+ const json = await readGitHubJson(resp);
233
+ if (!resp.ok)
234
+ throw githubError(json, resp.status, 'GitHub Pages branch lookup failed.');
235
+ const object = json.object;
236
+ return typeof object?.sha === 'string' ? object.sha : undefined;
237
+ }
238
+ async function createGitHubBlob(config, owner, repo, data) {
239
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/blobs`, redirectGuardInit({
240
+ method: 'POST',
241
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
242
+ body: JSON.stringify({ content: Buffer.from(data).toString('base64'), encoding: 'base64' }),
243
+ }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
244
+ assertNotRedirected(resp, 'GitHub blob creation');
245
+ const json = await readGitHubJson(resp);
246
+ if (!resp.ok)
247
+ throw githubError(json, resp.status, 'GitHub blob creation failed.');
248
+ const sha = typeof json.sha === 'string' ? json.sha : '';
249
+ if (!sha)
250
+ throw new DeployError('GitHub blob response did not include a sha.', 502, json);
251
+ return sha;
252
+ }
253
+ /**
254
+ * Creates one blob per distinct file content (deduped by a local sha256 key
255
+ * — a plain dedup key, not a wire-protocol requirement the way Netlify's
256
+ * SHA1 manifest is, so any stable collision-resistant digest works; sha256
257
+ * via `node:crypto` matches `cloudflare-pages.ts`'s own no-new-dependency
258
+ * choice for the same class of problem) and assembles the resulting tree in
259
+ * one call. Deliberately always creates real blobs rather than using the
260
+ * tree endpoint's inline `content` shortcut — that shortcut only accepts
261
+ * text content, and a static site's asset set (images, fonts, ...) is not
262
+ * reliably valid UTF-8.
263
+ */
264
+ async function createGitHubTreeFromFiles(config, owner, repo, files) {
265
+ const blobShaByHash = new Map();
266
+ const tree = [];
267
+ for (const file of files) {
268
+ const hash = sha256Hex(file.data);
269
+ let blobSha = blobShaByHash.get(hash);
270
+ if (!blobSha) {
271
+ blobSha = await createGitHubBlob(config, owner, repo, file.data);
272
+ blobShaByHash.set(hash, blobSha);
273
+ }
274
+ tree.push({ path: file.file, mode: '100644', type: 'blob', sha: blobSha });
275
+ }
276
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/trees`, redirectGuardInit({
277
+ method: 'POST',
278
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
279
+ body: JSON.stringify({ tree }),
280
+ }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
281
+ assertNotRedirected(resp, 'GitHub tree creation');
282
+ const json = await readGitHubJson(resp);
283
+ if (!resp.ok)
284
+ throw githubError(json, resp.status, 'GitHub tree creation failed.');
285
+ const sha = typeof json.sha === 'string' ? json.sha : '';
286
+ if (!sha)
287
+ throw new DeployError('GitHub tree response did not include a sha.', 502, json);
288
+ return sha;
289
+ }
290
+ async function createGitHubCommit(config, owner, repo, message, treeSha, parents) {
291
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/commits`, redirectGuardInit({
292
+ method: 'POST',
293
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
294
+ body: JSON.stringify({ message, tree: treeSha, parents }),
295
+ }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
296
+ assertNotRedirected(resp, 'GitHub commit creation');
297
+ const json = await readGitHubJson(resp);
298
+ if (!resp.ok)
299
+ throw githubError(json, resp.status, 'GitHub commit creation failed.');
300
+ const sha = typeof json.sha === 'string' ? json.sha : '';
301
+ if (!sha)
302
+ throw new DeployError('GitHub commit response did not include a sha.', 502, json);
303
+ return sha;
304
+ }
305
+ async function createGitHubRef(config, owner, repo, branch, sha) {
306
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/refs`, redirectGuardInit({
307
+ method: 'POST',
308
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
309
+ body: JSON.stringify({ ref: `refs/heads/${branch}`, sha }),
310
+ }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
311
+ assertNotRedirected(resp, 'GitHub Pages branch creation');
312
+ const json = await readGitHubJson(resp);
313
+ if (!resp.ok)
314
+ throw githubError(json, resp.status, 'GitHub Pages branch creation failed.');
315
+ }
316
+ /**
317
+ * `force: true` even though `sha` is always a direct child of the branch's
318
+ * current tip (`createGitHubCommit`'s `parents` was seeded from this exact
319
+ * ref lookup, so this update is a genuine fast-forward): a concurrent
320
+ * publish racing between this file's own ref-lookup and this update could
321
+ * otherwise turn a legitimate fast-forward into a rejected non-fast-forward
322
+ * update (422). `force: true` trades that narrow race for the same
323
+ * "last publish wins" semantics every other target in this package already
324
+ * has (a second concurrent `publish()` call also just overwrites whatever
325
+ * the first one produced).
326
+ */
327
+ async function updateGitHubRef(config, owner, repo, branch, sha) {
328
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/git/refs/heads/${enc(branch)}`, redirectGuardInit({
329
+ method: 'PATCH',
330
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
331
+ body: JSON.stringify({ sha, force: true }),
332
+ }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
333
+ assertNotRedirected(resp, 'GitHub Pages branch update');
334
+ const json = await readGitHubJson(resp);
335
+ if (!resp.ok)
336
+ throw githubError(json, resp.status, 'GitHub Pages branch update failed.');
337
+ }
338
+ /**
339
+ * Finds the repository's Pages site config, creating it (pointed at
340
+ * `branch`, `build_type: 'legacy'` — the branch-based build model, matching
341
+ * this file's Git Data API publish mechanism rather than the Actions
342
+ * `'workflow'` build type) if none exists yet. Never mutates an
343
+ * already-enabled site's configuration — see `publish()`'s
344
+ * `sourceBranchMismatch` handling for what happens when the existing site
345
+ * is configured against a different branch than this call targets.
346
+ */
347
+ async function ensureGitHubPagesSite(config, owner, repo, branch) {
348
+ const getResp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/pages`, redirectGuardInit({ headers: githubHeaders(config.token) }), { timeoutMs: FETCH_TIMEOUT_MS.QUICK });
349
+ assertNotRedirected(getResp, 'GitHub Pages site lookup');
350
+ if (getResp.status === 404) {
351
+ const createResp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/pages`, redirectGuardInit({
352
+ method: 'POST',
353
+ headers: githubHeaders(config.token, { 'Content-Type': 'application/json' }),
354
+ body: JSON.stringify({ source: { branch, path: '/' }, build_type: 'legacy' }),
355
+ }), { timeoutMs: FETCH_TIMEOUT_MS.DEPLOY });
356
+ assertNotRedirected(createResp, 'GitHub Pages site creation');
357
+ const created = await readGitHubJson(createResp);
358
+ if (!createResp.ok)
359
+ throw githubError(created, createResp.status, 'GitHub Pages site creation failed.');
360
+ return created;
361
+ }
362
+ const json = await readGitHubJson(getResp);
363
+ if (!getResp.ok)
364
+ throw githubError(json, getResp.status, 'GitHub Pages site lookup failed.');
365
+ return json;
366
+ }
367
+ async function readPagesBuildResponse({ resp }, _options = {}) {
368
+ if (resp.status === 404)
369
+ return undefined;
370
+ let json;
371
+ try {
372
+ json = await readGitHubJson(resp);
373
+ }
374
+ catch {
375
+ // Same "nothing usable to report yet" bucket as the 404 branch above, not a
376
+ // publish failure: every step up to and including enabling the Pages site has
377
+ // already irreversibly succeeded by the time this loop runs (see `publish()`'s
378
+ // call order), so a response this file cannot parse as JSON — an empty/no-content
379
+ // body while GitHub's build-tracking record has not been written yet, or a
380
+ // transient gateway/edge response — is exactly as uninformative as a 404, never a
381
+ // reason to report the whole publish as failed. A genuinely persistent problem
382
+ // still surfaces: this loop is bounded by the same fixed 30-attempt budget as every
383
+ // other outcome (see `proceeds to the reachability wait ... exhausts its 30-attempt
384
+ // budget`), after which `publish()` falls through to the reachability probe with
385
+ // whatever URL it already has — never a false "published: false" for a site that is
386
+ // actually live (`github-pages.test.ts`'s `keeps polling past a builds/latest
387
+ // response that fails to parse as JSON` regression test).
388
+ return undefined;
389
+ }
390
+ if (!resp.ok)
391
+ throw githubError(json, resp.status, 'GitHub Pages build status check failed.');
392
+ return { json };
393
+ }
394
+ function pagesBuildState({ json }, _options = {}) {
395
+ const status = typeof json.status === 'string' ? json.status : '';
396
+ const errorObj = json.error;
397
+ const errorMessage = typeof errorObj?.message === 'string' ? errorObj.message : undefined;
398
+ return { status, ...(errorMessage ? { errorMessage } : {}), raw: json };
399
+ }
400
+ function isPagesBuildForCommit({ json, commitSha }, _options = {}) {
401
+ const commit = typeof json.commit === 'string' ? json.commit : '';
402
+ return commit === commitSha;
403
+ }
404
+ /**
405
+ * Polls `GET /repos/{owner}/{repo}/pages/builds/latest` — deliberately not
406
+ * the Pages site resource's own `status` field (`GET
407
+ * /repos/{owner}/{repo}/pages`), even though that field is documented with
408
+ * a cleaner confirmed enum. Reason: the site-level `status` is shared
409
+ * across every publish to the same site, so immediately after pushing a new
410
+ * commit it can still reflect the *previous* build's terminal state for a
411
+ * moment — a false-positive "already built" race with no way to tell it
412
+ * apart from a genuinely finished new build. `builds/latest`'s `commit`
413
+ * field lets this function confirm it is actually looking at *this*
414
+ * publish's build (`commit === commitSha`) before trusting its `status` at
415
+ * all, closing that race at the cost of committing to a smaller slice of
416
+ * that field's enum (see `GITHUB_PAGES_BUILD_SUCCESS`/`_FAILURE`'s doc
417
+ * comment for why only two values are treated as terminal).
418
+ *
419
+ * A 404 (no build has run yet — expected immediately after a brand-new
420
+ * site's first-ever push/enable, undocumented but consistent with GitHub
421
+ * REST's general "resource doesn't exist yet" convention), a stale,
422
+ * non-matching `commit`, and a response this file cannot parse as JSON are
423
+ * ALL treated as "keep polling," not an error — see the `catch` below for
424
+ * why the non-JSON case joins that list. Any other non-2xx response whose
425
+ * body DID parse still fails fast, matching
426
+ * `pollVercelDeployment`/`pollNetlifyDeploy`'s existing convention. Same
427
+ * fixed 30-attempt/1s-then-2s budget as this package's other targets.
428
+ */
429
+ async function pollGitHubPagesBuild(config, owner, repo, commitSha) {
430
+ let last = null;
431
+ for (let i = 0; i < 30; i += 1) {
432
+ await sleep(i < 5 ? 1000 : 2000);
433
+ const resp = await fetchWithTimeout(`${GITHUB_API}/repos/${enc(owner)}/${enc(repo)}/pages/builds/latest`, redirectGuardInit({ headers: githubHeaders(config.token) }), { timeoutMs: FETCH_TIMEOUT_MS.DEPLOY });
434
+ assertNotRedirected(resp, 'GitHub Pages build status check');
435
+ const read = await readPagesBuildResponse({ resp });
436
+ const json = read?.json;
437
+ if (read === undefined || !isPagesBuildForCommit({ json, commitSha }))
438
+ continue;
439
+ last = pagesBuildState({ json });
440
+ const { status } = last;
441
+ if (status === GITHUB_PAGES_BUILD_SUCCESS || status === GITHUB_PAGES_BUILD_FAILURE)
442
+ return last;
443
+ }
444
+ return last;
445
+ }
446
+ /** Collects candidate public URLs from a Pages site response, preferring `html_url` (the browser-facing field, standard GitHub REST convention) over the bare `url` field (the API resource URL, which happens to also be public-reachable for Pages but is documented separately). */
447
+ function githubPagesUrlCandidates(site) {
448
+ const urls = [];
449
+ if (site && typeof site.html_url === 'string')
450
+ urls.push(site.html_url);
451
+ if (site && typeof site.url === 'string')
452
+ urls.push(site.url);
453
+ return [...new Set(urls.map(normalizeDeploymentUrl).filter(Boolean))];
454
+ }
455
+
456
+ return { GitHubPagesDeployTarget };
457
+ }
458
+
459
+ /** @param {unknown} value */
460
+ function asText(value) {
461
+ return typeof value === "string" ? value : "";
462
+ }
463
+
464
+ /**
465
+ * Reads one credential-check response WITHOUT touching its body: 401/403 means the host refused the
466
+ * credential, any other failure says nothing about it (a 5xx, a rate limit).
467
+ *
468
+ * @param {Response} response
469
+ * @returns {{ ok: true } | { ok: false, reason: "rejected" | "unreachable", statusCode: number }}
470
+ */
471
+ function classifyCredentialResponse(response) {
472
+ if (response.ok) return { ok: true };
473
+ return { ok: false, reason: response.status === 401 || response.status === 403 ? "rejected" : "unreachable", statusCode: response.status };
474
+ }
475
+
476
+ /**
477
+ * The token's account `login` from a `GET /user` body, or `undefined`. Reads no other field.
478
+ * @param {unknown} body
479
+ * @returns {string | undefined}
480
+ */
481
+ function readGitHubLogin(body) {
482
+ const login = typeof body === "object" && body !== null ? /** @type {Record<string, unknown>} */ (body).login : undefined;
483
+ return typeof login === "string" && login !== "" ? login : undefined;
484
+ }
485
+
486
+ /** The module contract a host's deploy-target registry loads (`DeployTargetModule`). Config fields:
487
+ * `owner`, `repo`, optional `branch` (the class defaults it to `gh-pages`). */
488
+ export default {
489
+ /** @param {{ credential: { token: string }, config: { owner?: string, repo?: string, branch?: string }, kit: any }} context */
490
+ create({ credential, config, kit }) {
491
+ const { GitHubPagesDeployTarget } = bindGitHubPages(kit);
492
+ class NoJekyllGitHubPagesDeployTarget extends GitHubPagesDeployTarget {
493
+ /** @param {{ files: Array<{ file: string, data: string | Uint8Array }>, projectName: string }} input */
494
+ publish(input) {
495
+ return super.publish({ ...input, files: withNoJekyll(input.files) });
496
+ }
497
+ }
498
+ return new NoJekyllGitHubPagesDeployTarget({
499
+ token: credential.token,
500
+ owner: asText(config.owner),
501
+ repo: asText(config.repo),
502
+ ...(typeof config.branch === "string" ? { branch: config.branch } : {}),
503
+ });
504
+ },
505
+ /**
506
+ * `GET /user`, GitHub's own "who am I" endpoint: 401 for a bad or revoked token. Reads back only
507
+ * `login`, the public handle GitHub prints in every profile and repo URL (a publish is about to
508
+ * print it in `https://<login>.github.io/<repo>/`), never email, plan or orgs.
509
+ * @param {{ credential: { token: string }, kit: any }} context
510
+ */
511
+ async verifyCredential({ credential, kit }) {
512
+ const response = await kit.fetch("https://api.github.com/user", { headers: { Authorization: `Bearer ${credential.token}`, Accept: "application/vnd.github+json" } }, { timeoutMs: kit.timeouts.QUICK });
513
+ const check = classifyCredentialResponse(response);
514
+ if (!check.ok) return check;
515
+ return { ok: true, accountLabel: readGitHubLogin(await response.json().catch(() => undefined)) };
516
+ },
517
+ /**
518
+ * Shape check at the boundary before any value reaches a URL (the class still escapes each
519
+ * segment). Texts are byte-identical to the checks the first host (Tovu) ran in its core before this moved here.
520
+ * @param {{ owner?: unknown, repo?: unknown, branch?: unknown }} config
521
+ * @returns {string | null}
522
+ */
523
+ validateConfig(config) {
524
+ const owner = asText(config.owner);
525
+ const repo = asText(config.repo);
526
+ if (!OWNER_PATTERN.test(owner)) return `invalid GitHub owner '${owner.slice(0, 60)}'`;
527
+ if (!REPO_PATTERN.test(repo) || repo === "." || repo === "..") return `invalid GitHub repo '${repo.slice(0, 100)}'`;
528
+ if (config.branch !== undefined && !BRANCH_PATTERN.test(asText(config.branch))) return `invalid branch name '${asText(config.branch).slice(0, 60)}'`;
529
+ return null;
530
+ },
531
+ /** A project site is served from `/<repo>`, so every asset URL must carry that prefix.
532
+ * @param {{ repo?: unknown }} config */
533
+ basePath(config) {
534
+ return `/${asText(config.repo)}`;
535
+ },
536
+ /** The confirmation card reads `owner/repo` as one row, the way GitHub prints a repository.
537
+ * @param {{ owner?: unknown, repo?: unknown, branch?: unknown }} config */
538
+ summarize(config) {
539
+ const rows = [{ label: "Repository", value: `${asText(config.owner)}/${asText(config.repo)}` }];
540
+ if (typeof config.branch === "string") rows.push({ label: "Branch", value: config.branch });
541
+ return rows;
542
+ },
543
+ };