@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,287 @@
1
+ /**
2
+ * @file S3-compatible `ObjectStoreProvider` (`@jini-ai/core/object-store`, provider id `s3_compatible`)
3
+ * for the bundled s3-compatible Agent Plugin: AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean
4
+ * Spaces, Wasabi, MinIO, ...
5
+ *
6
+ * Moved from Tovu core `apps/website/src/features/media/blob-store.s3.ts` (`S3BlobStore`): same
7
+ * requests, same path-style object URLs, same status handling, error texts re-prefixed
8
+ * `S3ObjectStore.<method>`. SigV4 signing comes from the host (`host.createSigV4Client`, the same
9
+ * `aws4fetch` `AwsClient` core used), because an installed plugin has no npm dependencies; the host
10
+ * may wrap the signed fetch (`host.wrapFetch`) to trace each object call. Plain JS by owner decision
11
+ * (2026-09-29), like `targets/s3-compatible.mjs`.
12
+ *
13
+ * Bucket posture (carried over from `S3BlobStore`): this store signs key → bytes operations against
14
+ * a bucket the Tovu server itself is the ONLY reader of — every public media byte is proxied back out
15
+ * through the `/m/...` rendition route, which calls the store's `get()` and streams the result itself.
16
+ * That proxying is what makes this bucket safe to leave PRIVATE (least-privilege
17
+ * `s3:GetObject`/`PutObject`/`DeleteObject`/`HeadObject` on one prefix, no `s3:ListBucket`, no
18
+ * public-read ACL, no CORS, no website hosting, no custom domain) — intentionally the opposite posture
19
+ * from the publish target's bucket (`targets/s3-compatible.mjs`), so a reader who has seen that
20
+ * target's config shape should not assume the two need the same bucket setup. Hence no
21
+ * `publicAccess` on this store; shares go through {@link createArtifactStore}, the publish target's
22
+ * own public-bucket capability.
23
+ *
24
+ * Object keys (the no-data-loss rule): the S3 object key is `keyPrefix + key`, byte-identical to
25
+ * `S3BlobStore`'s `keyPrefix + storageKey`, and the URL is built segment by segment exactly as its
26
+ * `objectUrl` was. A site switching from `S3BlobStore` to this provider therefore addresses the exact
27
+ * same objects the SQLite `asset_blobs.storage_key` column already records; only what interprets that
28
+ * string changes. Changing this derivation would orphan every existing upload.
29
+ *
30
+ * Every data-plane operation is one signed HTTP request against
31
+ * `{endpoint-or-derived-AWS-host}/{bucket}/{key}` — no retries, no multipart (the objects this store
32
+ * handles are single-file media uploads, not the multi-GB objects multipart exists for).
33
+ */
34
+
35
+ import deployTargetModule from "../targets/s3-compatible.mjs";
36
+
37
+ /** `get` bound, as `S3BlobStore.get` had: a whole media object, so generous. */
38
+ const GET_TIMEOUT_MS = 120_000;
39
+ /** `head` bound, as `S3BlobStore.sizeOf` had: no payload, so short. */
40
+ const HEAD_TIMEOUT_MS = 30_000;
41
+
42
+ /**
43
+ * @typedef {{ readonly bucket: string, readonly region: string, readonly endpoint?: string, readonly keyPrefix?: string }} S3ObjectStoreConfig
44
+ * @typedef {(url: string, init: RequestInit) => Promise<Response>} SendFn
45
+ * @typedef {{ createSigV4Client(input: { accessKeyId: string, secretAccessKey: string, service: string, region: string }): { fetch: SendFn }, wrapFetch?: (fetchFn: SendFn) => SendFn }} S3ObjectStoreHost
46
+ */
47
+
48
+ /**
49
+ * The setting/credential names `create` refuses without, each with how to read it. A blank string is
50
+ * missing too — the same `!value` test Tovu's env path (`TOVU_S3_*`) applies.
51
+ */
52
+ const REQUIRED_INPUTS = [
53
+ ["credential.fields.accessKeyId", ({ credential }) => credential?.fields?.accessKeyId],
54
+ ["credential.fields.region", ({ credential }) => credential?.fields?.region],
55
+ ["credential.secret", ({ credential }) => credential?.secret],
56
+ ["settings.bucket", ({ settings }) => settings?.bucket],
57
+ ];
58
+
59
+ /**
60
+ * @param {{ settings?: Readonly<Record<string, string | undefined>>, credential?: { fields?: Readonly<Record<string, string | undefined>>, secret?: string } }} required
61
+ * @returns {string[]} The names of every missing required input, in {@link REQUIRED_INPUTS} order.
62
+ * @complexity O(1) — a fixed list.
63
+ */
64
+ function missingInputs(required) {
65
+ return REQUIRED_INPUTS.filter(([, read]) => !read(required)).map(([name]) => name);
66
+ }
67
+
68
+ /**
69
+ * Blank (or whitespace-only) derives a plain-AWS-S3 host from the region — byte-identical to
70
+ * `S3BlobStore`'s and the publish target's `deriveEndpoint`. Trailing slashes are stripped.
71
+ * @param {{ endpoint?: string, region: string }} required
72
+ * @complexity O(n) in the endpoint length.
73
+ */
74
+ function resolveHost(required) {
75
+ const endpoint = required.endpoint?.trim();
76
+ return stripTrailingSlashes({ value: endpoint ? endpoint : `https://s3.${required.region}.amazonaws.com` });
77
+ }
78
+
79
+ /**
80
+ * Same result as `.replace(/\/+$/, "")`, without that regex's backtracking on a long run of slashes.
81
+ * @param {{ value: string }} required
82
+ * @complexity O(n) in the value length.
83
+ */
84
+ function stripTrailingSlashes(required) {
85
+ const { value } = required;
86
+ let end = value.length;
87
+ while (end > 0 && value[end - 1] === "/") end -= 1;
88
+ return value.slice(0, end);
89
+ }
90
+
91
+ /**
92
+ * Builds the path-style object URL for one key, each path segment percent-encoded independently
93
+ * (mirrors `S3BlobStore`'s and `targets/s3-compatible.mjs`'s `objectUrl`).
94
+ * @param {{ host: string, config: S3ObjectStoreConfig, key: string }} required
95
+ * @complexity O(n) in the number of path segments.
96
+ */
97
+ function objectUrl(required) {
98
+ const { host, config, key } = required;
99
+ const fullKey = `${config.keyPrefix ?? ""}${key}`;
100
+ const encodedKey = fullKey
101
+ .split("/")
102
+ .map((segment) => encodeURIComponent(segment))
103
+ .join("/");
104
+ return `${host}/${encodeURIComponent(config.bucket)}/${encodedKey}`;
105
+ }
106
+
107
+ /**
108
+ * Reads a response body defensively for an error message — never throws itself, capped at 200
109
+ * characters since these messages are logged/thrown locally rather than surfaced to a user.
110
+ * @param {{ response: Response }} required
111
+ * @complexity O(body).
112
+ */
113
+ async function safeErrorBody(required) {
114
+ try {
115
+ return (await required.response.text()).slice(0, 200);
116
+ } catch {
117
+ return "";
118
+ }
119
+ }
120
+
121
+ /**
122
+ * The caller's abort signal, bounded by `timeoutMs` when one is given.
123
+ * @param {{ signal?: AbortSignal }} required
124
+ * @param {{ timeoutMs?: number }} [optional]
125
+ * @returns {{ signal?: AbortSignal }} A `RequestInit` fragment (empty when there is nothing to bound).
126
+ * @complexity O(1).
127
+ */
128
+ function signalInit(required, optional = {}) {
129
+ const { signal } = required;
130
+ const { timeoutMs } = optional;
131
+ if (timeoutMs === undefined) return signal ? { signal } : {};
132
+ const timeout = AbortSignal.timeout(timeoutMs);
133
+ return { signal: signal ? AbortSignal.any([signal, timeout]) : timeout };
134
+ }
135
+
136
+ /**
137
+ * The request headers for one `put`: caller headers, then `content-type`, then the create-only
138
+ * precondition — so neither of the latter two can be overridden by a stray caller header.
139
+ * A native `Headers` with `.set()`, not an object spread: header names are case-insensitive, so a
140
+ * caller's `if-none-match` and an enforced `If-None-Match` would otherwise both be sent and combine
141
+ * into one `etag-1, *` value instead of the required `*`.
142
+ * @param {{ contentType?: string, headers?: Readonly<Record<string, string>>, ifAbsent?: boolean }} required
143
+ * @returns {Headers}
144
+ * @complexity O(headers).
145
+ */
146
+ function putHeaders(required) {
147
+ const { contentType, headers, ifAbsent } = required;
148
+ const merged = new Headers(headers);
149
+ if (contentType !== undefined) merged.set("content-type", contentType);
150
+ if (ifAbsent) merged.set("if-none-match", "*");
151
+ return merged;
152
+ }
153
+
154
+ /**
155
+ * Builds the store over one signing `send`.
156
+ * @param {{ config: S3ObjectStoreConfig, send: SendFn }} required
157
+ * @returns {import("@jini-ai/core/object-store").ObjectStore}
158
+ * @complexity O(1) to build; each call is one signed HTTP request.
159
+ */
160
+ function createS3ObjectStore(required) {
161
+ const { config, send } = required;
162
+ const host = resolveHost(config);
163
+ /**
164
+ * One signed request; a transport failure rejects as `S3ObjectStore.<method>: request failed`.
165
+ * @param {{ method: string, op: string, key: string, init: RequestInit }} call
166
+ */
167
+ const request = async (call) => {
168
+ try {
169
+ return await send(objectUrl({ host, config, key: call.key }), { method: call.method, ...call.init });
170
+ } catch (err) {
171
+ throw new Error(`S3ObjectStore.${call.op}: request failed for '${call.key}' — ${err instanceof Error ? err.message : String(err)}`);
172
+ }
173
+ };
174
+
175
+ return {
176
+ /**
177
+ * `ifAbsent` sends `If-None-Match: *`, making this a conditional PUT: the object store itself
178
+ * refuses the write (HTTP 412) when the key is already occupied, so there is no separate `head()`
179
+ * round trip and therefore no window between "check" and "write" for a concurrent writer to land
180
+ * in — the same atomicity guarantee the local store's `"wx"` flag gets from the filesystem.
181
+ *
182
+ * Provider caveat (disclosed, not silently assumed): AWS S3 and Cloudflare R2 honor
183
+ * `If-None-Match: *` on `PUT`; some other S3-compatible providers (older MinIO, some DigitalOcean
184
+ * Spaces/Wasabi deployments) may not enforce it and could return 200 on an unconditional overwrite
185
+ * instead of 412 — on those providers specifically this degrades to an unconditional overwrite,
186
+ * not a crash or a silently wrong `written` value for the providers that do enforce it.
187
+ */
188
+ async put({ key, bytes }, options = {}) {
189
+ const resp = await request({
190
+ method: "PUT",
191
+ op: "put",
192
+ key,
193
+ init: { headers: putHeaders(options), body: bytes, ...signalInit(options) },
194
+ });
195
+ if (options.ifAbsent && resp.status === 412) {
196
+ // Precondition Failed — an object already occupies this key. Content-addressed callers
197
+ // (media blobs are keyed by sha256) know whatever is there carries the same bytes this call
198
+ // would have written, so leaving it untouched is always correct.
199
+ return { written: false };
200
+ }
201
+ if (!resp.ok) {
202
+ const body = await safeErrorBody({ response: resp });
203
+ const bodySuffix = body ? ` — ${body}` : "";
204
+ throw new Error(`S3ObjectStore.put: failed to write '${key}' — HTTP ${resp.status}${bodySuffix}`);
205
+ }
206
+ return { written: true };
207
+ },
208
+
209
+ /** `null` only on a genuine 404; every other non-ok status rejects. */
210
+ async get({ key }, options = {}) {
211
+ const resp = await request({ method: "GET", op: "get", key, init: signalInit(options, { timeoutMs: GET_TIMEOUT_MS }) });
212
+ if (resp.status === 404) return null;
213
+ if (!resp.ok) throw new Error(`S3ObjectStore.get: failed to read '${key}' — HTTP ${resp.status}`);
214
+ return new Uint8Array(await resp.arrayBuffer());
215
+ },
216
+
217
+ /**
218
+ * A 404 means the object is genuinely absent and resolves to `null`. Any other non-ok status
219
+ * (403/429/5xx — an auth failure, rate limit, or provider outage) rejects instead of collapsing
220
+ * into `null`: a caller branching on presence must not mistake "the store rejected/failed this
221
+ * request" for "the object was deleted". A bounded exporter needs the size BEFORE buffering an
222
+ * object, so a missing or malformed `content-length` rejects rather than guessing.
223
+ */
224
+ async head({ key }, options = {}) {
225
+ const resp = await request({ method: "HEAD", op: "head", key, init: signalInit(options, { timeoutMs: HEAD_TIMEOUT_MS }) });
226
+ if (resp.status === 404) return null;
227
+ if (!resp.ok) throw new Error(`S3ObjectStore.head: failed to check '${key}' — HTTP ${resp.status}`);
228
+ const length = resp.headers.get("content-length");
229
+ if (length === null || !/^\d+$/.test(length) || !Number.isSafeInteger(Number(length))) {
230
+ throw new Error("S3ObjectStore.head: storage did not return a valid content length");
231
+ }
232
+ return { size: Number(length) };
233
+ },
234
+
235
+ /** Idempotent — a 404 (already absent) is success, the same tolerance the local store gets from `rm({ force: true })`. */
236
+ async delete({ key }, options = {}) {
237
+ const resp = await request({ method: "DELETE", op: "delete", key, init: signalInit(options) });
238
+ if (!resp.ok && resp.status !== 404) {
239
+ throw new Error(`S3ObjectStore.delete: failed to delete '${key}' — HTTP ${resp.status}`);
240
+ }
241
+ },
242
+ };
243
+ }
244
+
245
+ /** The module contract the host's object-store registry loads (`ObjectStoreProvider`). */
246
+ export default {
247
+ /**
248
+ * @param {{ settings: Readonly<Record<string, string | undefined>>, credential?: { fields: Readonly<Record<string, string | undefined>>, secret: string }, host: S3ObjectStoreHost }} required
249
+ * `credential.fields`: `accessKeyId`, `region`, optional `endpoint`; `credential.secret`: the secret
250
+ * access key; `settings`: `bucket`, optional `keyPrefix`.
251
+ * @param {Record<string, never>} [_optional]
252
+ * @returns {{ status: "ok", value: import("@jini-ai/core/object-store").ObjectStore } | { status: "rejected", error: { code: "not_configured", message: string } }}
253
+ * Never throws for a missing input: the host reports the refusal instead of crashing.
254
+ */
255
+ create(required, _optional = {}) {
256
+ const missing = missingInputs(required);
257
+ if (missing.length > 0) {
258
+ return {
259
+ status: "rejected",
260
+ error: { code: "not_configured", message: `S3-compatible object store is missing required input(s): ${missing.join(", ")}.` },
261
+ };
262
+ }
263
+ const { settings, credential, host } = required;
264
+ const { accessKeyId, region, endpoint } = credential.fields;
265
+ const client = host.createSigV4Client({ accessKeyId, secretAccessKey: credential.secret, service: "s3", region });
266
+ const signed = (url, init) => client.fetch(url, init);
267
+ const send = host.wrapFetch ? host.wrapFetch(signed) : signed;
268
+ const config = {
269
+ bucket: settings.bucket,
270
+ region,
271
+ ...(endpoint === undefined ? {} : { endpoint }),
272
+ ...(settings.keyPrefix === undefined ? {} : { keyPrefix: settings.keyPrefix }),
273
+ };
274
+ return { status: "ok", value: createS3ObjectStore({ config, send }) };
275
+ },
276
+
277
+ /**
278
+ * The public share capability, delegated to this plugin's own publish target
279
+ * (`targets/s3-compatible.mjs`) so a share can be resolved through the object-store registry while
280
+ * the public-bucket code (key shape check, public URL, anonymous verify) keeps one owner.
281
+ * @param {{ credential: Record<string, string | undefined> & { token: string }, kit: any }} required
282
+ * @param {Record<string, never>} [optional]
283
+ */
284
+ createArtifactStore(required, optional = {}) {
285
+ return deployTargetModule.createArtifactStore(required, optional);
286
+ },
287
+ };
@@ -0,0 +1,56 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "s3-compatible",
4
+ "version": "0.1.0",
5
+ "description": "Publishes the site's static export to S3-compatible object storage (AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean Spaces, Wasabi, MinIO, ...) with the workspace's own saved access key. 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: what to set up in the bucket first, which credential fields go where, and what each failure means in plain words.",
6
+ "author": "Tovu",
7
+ "license": "Apache-2.0",
8
+ "extensions": {
9
+ "tovu": {
10
+ "maturity": "soon",
11
+ "vendorAliases": ["s3_compatible", "s3 compatible", "s3compatible", "amazonaws", "backblaze", "wasabi", "digitaloceanspaces"],
12
+ "displayName": "S3",
13
+ "summary": "Puts your website online in your own storage bucket, like Amazon S3 or Cloudflare R2. The assistant shows you a preview first.",
14
+ "id": "tovu/s3-compatible",
15
+ "provides": [
16
+ "deploy-target:s3-compatible"
17
+ ],
18
+ "credentialCards": [
19
+ {
20
+ "target": "s3-compatible",
21
+ "title": "Connect S3-compatible storage",
22
+ "label": "S3-compatible storage connection",
23
+ "category": "hosting",
24
+ "intro": "Connect your storage provider to upload this site to an existing public bucket. Create the bucket and public hosting first.",
25
+ "usedFor": [
26
+ "Publishes your website to your public storage bucket",
27
+ "Stores and retrieves site media in your configured private bucket"
28
+ ],
29
+ "keyHint": "Use an access key ID and secret access key limited to the selected bucket: ListBucket and GetObject, PutObject, DeleteObject on its objects. If using a separate private media bucket, grant only the required read/write access there too.",
30
+ "saveKind": "publish-host",
31
+ "baseUrl": "https://s3.amazonaws.com"
32
+ }
33
+ ],
34
+ "toolKeywords": {
35
+ "media_import_from_url": "s3",
36
+ "credential_save": "s3"
37
+ }
38
+ }
39
+ },
40
+ "keywords": [
41
+ "s3-compatible",
42
+ "s3",
43
+ "r2",
44
+ "b2",
45
+ "object-storage",
46
+ "bucket",
47
+ "deploy",
48
+ "publish",
49
+ "hosting",
50
+ "static-site",
51
+ "static-export",
52
+ "go-live",
53
+ "website",
54
+ "deploy-target:s3-compatible"
55
+ ]
56
+ }
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: s3-compatible
3
+ description: Publish the site's static export to S3-compatible storage (AWS S3, Cloudflare R2, Backblaze B2, DigitalOcean Spaces, Wasabi, MinIO, ...) with the workspace's own saved access key. Covers what the person sets up first, the credential fields, and what each failure means in plain words.
4
+ ---
5
+
6
+ # S3-compatible storage
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 S3-compatible storage** card with `credential_save {kind:"plugin-provider",target:"s3-compatible"}`. It collects the bucket, region, endpoint, access key ID, public URL and masked secret access key, plus optional private-media settings. Scope the key to the selected bucket. Key creation belongs to your storage provider’s console; no single key-creation URL covers all compatible services.
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 S3-compatible storage.
19
+
20
+ - **Set up first, by the person:** a bucket they already created, public access or hosting in front of
21
+ it, and an access key limited to that bucket. The host creates none of these. Offer the exact steps and
22
+ bucket policy for their provider; they apply them in the provider's console.
23
+ - **Credential:** access key ID and secret access key, plus bucket, region and public URL, all saved
24
+ together through the plugin card above. Endpoint is only for non-AWS providers (R2, B2, Spaces,
25
+ Wasabi, MinIO); plain AWS S3 leaves it blank.
26
+ - **Public URL:** the address people visit. The host checks it after every publish and reports it plainly
27
+ if it does not answer.
28
+ - **Old files:** the host keeps a manifest of the files it published, and removes only those that are no
29
+ longer in the site. Other objects in the bucket are never touched.
30
+ - **Security headers:** S3 static hosting has no header config, so none are sent.
31
+
32
+ ## Failure messages and what to tell the person
33
+
34
+ | Message | Meaning | Next step |
35
+ |---|---|---|
36
+ | `Failed to upload '...' to S3-compatible storage: HTTP 403` | The key cannot write to that bucket. | Check the key's bucket permissions, the bucket name and the region. |
37
+ | `Failed to upload '...' ... HTTP 404` | The bucket does not exist at that endpoint. | Check the bucket name, region and endpoint. |
38
+ | `... Tovu-managed object manifest ...` | The host could not read or update its list of published files, so it stopped rather than guess. | Retry once; if it repeats, check the key can read and write the bucket. |
39
+ | `Concurrent publish detected ...` | Another publish changed the bucket at the same time. | Publish again. |