@jini-ai/agent-plugins 0.6.17 → 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 +29 -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,87 @@
1
+ /**
2
+ * @file "Choose spreadsheet" in the browser, shared by the Picker panel and the form-destination
3
+ * panel. Resolves with the server-VERIFIED spreadsheet metadata, or null when the person cancelled
4
+ * (nothing is saved on cancel).
5
+ *
6
+ * Hosted: loads Google's JS Picker (the one script origin the surface's CSP allows) and opens it with
7
+ * the bootstrap's short-lived access token and public config, spreadsheets only, one file. The file
8
+ * id it returns is only a claim: it goes to `verify-picked` with the Picker session id, and only the
9
+ * server's answer is used.
10
+ *
11
+ * Desktop: the native Picker runs in the system browser. `start-system-picker` makes the server start
12
+ * that sign-in (the admin page offers the link; this frame cannot open one), then the frame polls
13
+ * `finish-system-picker` until the server reports a verified selection, a cancel, or the expiry.
14
+ */
15
+ import { wait } from './dom.mjs';
16
+ import { pollText } from './picker-view.mjs';
17
+
18
+ const SPREADSHEET_MIME_TYPE = 'application/vnd.google-apps.spreadsheet';
19
+ const PICKER_SCRIPT = 'https://apis.google.com/js/api.js';
20
+ const POLL_MS = 3000;
21
+
22
+ /**
23
+ * Loads Google's Picker library once. Rejects when the script is blocked or fails, and forgets the
24
+ * failure so the next "Choose spreadsheet" (the message says "try again") loads it again.
25
+ */
26
+ let pickerLibrary;
27
+ function loadPickerLibrary() {
28
+ pickerLibrary ??= new Promise((resolve, reject) => {
29
+ const script = document.createElement('script');
30
+ script.src = PICKER_SCRIPT;
31
+ script.onerror = () => { pickerLibrary = undefined; reject(new Error('Google\'s file picker could not load (a blocked script, network, or browser setting). Allow apis.google.com and try again.')); };
32
+ script.onload = () => window.gapi.load('picker', { callback: () => resolve(window.google.picker), onerror: script.onerror });
33
+ document.head.append(script);
34
+ });
35
+ return pickerLibrary;
36
+ }
37
+
38
+ /** Opens the hosted Picker; resolves with the picked file id, or null on cancel. */
39
+ async function pickHostedFileId(config) {
40
+ const picker = await loadPickerLibrary();
41
+ return new Promise((resolve) => {
42
+ const view = new picker.DocsView(picker.ViewId.SPREADSHEETS).setMimeTypes(SPREADSHEET_MIME_TYPE);
43
+ const onEvent = (data) => {
44
+ const action = data[picker.Response.ACTION];
45
+ if (action === picker.Action.PICKED) resolve(data[picker.Response.DOCUMENTS]?.[0]?.[picker.Document.ID] ?? null);
46
+ else if (action === picker.Action.CANCEL) resolve(null);
47
+ else {
48
+ // Intermediate Picker events do not settle the choice.
49
+ }
50
+ };
51
+ new picker.PickerBuilder()
52
+ .setDeveloperKey(config.developerKey).setAppId(config.appId).setOAuthToken(config.accessToken)
53
+ .addView(view).setCallback(onEvent).build().setVisible(true);
54
+ });
55
+ }
56
+
57
+ /** Hosted: pick, then let the server verify. */
58
+ async function chooseHosted({ client, bootstrap }) {
59
+ const fileId = await pickHostedFileId(bootstrap.picker);
60
+ if (fileId === null) return null;
61
+ const { spreadsheet } = await client.call({ action: 'verify-picked', payload: { sessionId: bootstrap.picker.sessionId, fileId } });
62
+ return spreadsheet;
63
+ }
64
+
65
+ /** Desktop: start, then poll until selected, cancelled, expired, or the person stops waiting. */
66
+ async function chooseInSystemBrowser({ client, onStatus, stopped }) {
67
+ const { expiresAt } = await client.call({ action: 'start-system-picker', payload: {} });
68
+ onStatus(pollText({ status: 'waiting' }));
69
+ while (!stopped() && Date.now() < Date.parse(expiresAt) + POLL_MS) {
70
+ await wait(POLL_MS);
71
+ const outcome = await client.call({ action: 'finish-system-picker', payload: {} });
72
+ if (outcome.status === 'selected') return outcome.spreadsheet;
73
+ if (outcome.status !== 'waiting') { onStatus(pollText(outcome)); return null; }
74
+ }
75
+ onStatus(pollText({ status: stopped() ? 'cancelled' : 'expired' }));
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Runs "Choose spreadsheet" for the bootstrap's mode.
81
+ * @param {{ client: { call: Function }, bootstrap: { mode: string, picker?: object }, onStatus: (text: string) => void, stopped?: () => boolean }} required
82
+ * @returns {Promise<{ spreadsheetId: string, title: string, url: string } | null>} null = nothing chosen, nothing saved.
83
+ */
84
+ export function chooseSpreadsheet(required) {
85
+ const { client, bootstrap, onStatus, stopped = () => false } = required;
86
+ return bootstrap.mode === 'hosted' ? chooseHosted({ client, bootstrap }) : chooseInSystemBrowser({ client, onStatus, stopped });
87
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @file What "Choose spreadsheet" says at each step: pure text for the bootstrap modes and the desktop
3
+ * poll outcomes, shared by the Picker panel and the form-destination panel. No DOM, so the rule that
4
+ * a cancel changes nothing is stated (and testable) in one place.
5
+ */
6
+
7
+ /** Shown whenever a form destination is saved or removed: local deletes never purge Google's rows. */
8
+ export const EXTERNAL_ROWS_NOTE = 'Rows this site writes stay in the spreadsheet: removing this destination, or deleting the form or its submissions on this site, does not delete them.';
9
+
10
+ export const CANCELLED_TEXT = 'No spreadsheet was chosen. Your saved settings did not change.';
11
+
12
+ const POLL_TEXT = Object.freeze({
13
+ waiting: 'Choose the spreadsheet in your browser. Use the "Continue to sign-in" button the admin page shows above this panel; Google opens its file picker after the sign-in.',
14
+ expired: 'The file picker link expired before a spreadsheet was chosen. Your saved settings did not change; start again.',
15
+ none: 'No file picker is open. Start again.',
16
+ cancelled: CANCELLED_TEXT,
17
+ });
18
+
19
+ /**
20
+ * The line shown for a bootstrap mode before anything is chosen.
21
+ * @param {{ bootstrap: { mode: string, message?: string } | undefined }} required
22
+ * @returns {string}
23
+ */
24
+ export function pickerIntro(required, _optional = {}) {
25
+ const { bootstrap } = required;
26
+ if (bootstrap === undefined) return 'The panel did not receive its start data in time. Close it and open it again.';
27
+ if (bootstrap.mode === 'unavailable') return bootstrap.message ?? 'Google Sheets is not ready. Check the Google Workspace connection panel.';
28
+ if (bootstrap.mode === 'hosted') return 'Choose a Google Sheets spreadsheet. This site gets access to that one file only.';
29
+ return 'Choose a Google Sheets spreadsheet. Google\'s file picker opens in your browser; this site gets access to that one file only.';
30
+ }
31
+
32
+ /**
33
+ * The line for a desktop poll outcome (`selected` is rendered by the caller with the file's title).
34
+ * @param {{ status: 'waiting' | 'expired' | 'none' | 'cancelled' }} required
35
+ * @returns {string}
36
+ */
37
+ export function pollText(required, _optional = {}) {
38
+ return POLL_TEXT[required.status] ?? POLL_TEXT.none;
39
+ }
40
+
41
+ /** The line for a verified selection. */
42
+ export function selectedText(required, _optional = {}) {
43
+ return `Chosen: ${required.spreadsheet.title}.`;
44
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file Server-side handlers of the "Choose spreadsheet" panel (`ui/picker.html`), loaded by the host
3
+ * as this surface's `handlerModule` (never served to the frame).
4
+ *
5
+ * Exists so a person can grant the site one spreadsheet under `drive.file` the only way that grants it:
6
+ * Google's Picker (hosted: the JS Picker in the panel; desktop: the native Picker in the system
7
+ * browser). A selection is saved to the plugin's settings only after `lib/picker-flow.mjs` verified
8
+ * it (session, account, spreadsheet type, real Sheets access); the frame and chat get metadata only.
9
+ * A cancelled pick saves nothing.
10
+ */
11
+ import { createPickerFlow, metadataOf, pollReply, readPickedSpreadsheets, rememberPickedSpreadsheet } from '../lib/picker-flow.mjs';
12
+ import { withSurfaceFailures } from '../lib/surface-actions.mjs';
13
+
14
+ /**
15
+ * Builds the panel's handler module.
16
+ * @param {object} [_required]
17
+ * @param {{ now?: () => number, randomId?: () => string }} [optional] Passed to the Picker flow (tests).
18
+ * @returns {{ bootstrap: Function, actions: Record<string, Function> }} The `handlerModule` default export shape.
19
+ * @complexity O(1) to build; see each action.
20
+ */
21
+ export function createPickerHandlers(_required = {}, optional = {}) {
22
+ const flow = createPickerFlow({}, optional);
23
+ const listed = async (capabilities) => ({ spreadsheets: (await readPickedSpreadsheets({ state: capabilities.state })).map(metadataOf) });
24
+
25
+ return {
26
+ bootstrap: (call) => flow.bootstrap(call),
27
+ actions: {
28
+ /** The spreadsheets picked so far (metadata), newest first. */
29
+ 'list-picked': ({ capabilities, fail }) => withSurfaceFailures({ fail, run: () => listed(capabilities) }),
30
+
31
+ /** Hosted Picker result → verified → saved → metadata. */
32
+ 'verify-picked': (call) => withSurfaceFailures({
33
+ fail: call.fail,
34
+ run: async () => {
35
+ const spreadsheet = await flow.verifyHosted(call);
36
+ await rememberPickedSpreadsheet({ state: call.capabilities.state, spreadsheet });
37
+ return { spreadsheet: metadataOf(spreadsheet) };
38
+ },
39
+ }),
40
+
41
+ 'start-system-picker': (call) => withSurfaceFailures({ fail: call.fail, run: () => flow.startSystemBrowser(call) }),
42
+
43
+ /** Desktop poll; a verified selection is saved before it is reported. */
44
+ 'finish-system-picker': (call) => withSurfaceFailures({
45
+ fail: call.fail,
46
+ run: async () => {
47
+ const outcome = await flow.finishSystemBrowser(call);
48
+ if (outcome.status === 'selected') await rememberPickedSpreadsheet({ state: call.capabilities.state, spreadsheet: outcome.spreadsheet });
49
+ return pollReply({ outcome });
50
+ },
51
+ }),
52
+ },
53
+ };
54
+ }
55
+
56
+ export default createPickerHandlers();
@@ -0,0 +1,17 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Choose spreadsheet</title>
7
+ <!-- Static and secret-free: served without credentials. Start data arrives in the host's init message. -->
8
+ <link rel="stylesheet" href="panel.css">
9
+ <script type="module" src="picker.mjs"></script>
10
+ </head>
11
+ <body>
12
+ <main>
13
+ <h1 class="sr-only">Choose spreadsheet</h1>
14
+ <div id="app" aria-live="polite"><p class="muted">Loading…</p></div>
15
+ </main>
16
+ </body>
17
+ </html>
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @file The "Choose spreadsheet" panel in the browser: one button that runs the Picker flow, and the
3
+ * list of spreadsheets already chosen (metadata the server verified and saved). Their ids are what a
4
+ * person gives chat or the form-destination panel; a pasted link grants nothing.
5
+ */
6
+ import { connectToHost, failureText, h, notice, show, tip } from './dom.mjs';
7
+ import { chooseSpreadsheet } from './picker-flow.mjs';
8
+ import { CANCELLED_TEXT, pickerIntro, selectedText } from './picker-view.mjs';
9
+
10
+ let client;
11
+ let app;
12
+ let status;
13
+ /** 'info' while choosing or after a choice, 'error' after a failure (the status line's colour). */
14
+ let statusTone;
15
+ let waiting;
16
+ let stopRequested;
17
+ let picked;
18
+
19
+ function render(bootstrap) {
20
+ const ready = bootstrap !== undefined && bootstrap.mode !== 'unavailable';
21
+ // Ready: the intro is the heading's explanation and Choose sits in the header; otherwise the
22
+ // reason it is unavailable is the visible notice.
23
+ show(app,
24
+ ready ? null : h('p', { class: 'notice warn intro' }, [pickerIntro({ bootstrap })]),
25
+ status ? notice({ text: status, tone: statusTone }) : null,
26
+ h('section', { class: 'card', 'aria-labelledby': 'picked-heading' }, [
27
+ h('div', { class: 'head' }, [
28
+ h('h2', { id: 'picked-heading' }, ['Chosen spreadsheets']),
29
+ ready ? tip('About choosing a spreadsheet', pickerIntro({ bootstrap }), { bodyClass: 'intro' }) : null,
30
+ ready ? h('div', { class: 'actions end' }, [
31
+ h('button', { type: 'button', class: 'primary', disabled: waiting, onClick: () => choose(bootstrap) }, ['Choose spreadsheet']),
32
+ waiting && bootstrap.mode === 'system-browser' ? h('button', { type: 'button', onClick: () => { stopRequested = true; status = 'Stopping…'; render(bootstrap); } }, ['Stop waiting']) : null,
33
+ ]) : null,
34
+ ]),
35
+ picked.length === 0 ? h('p', { class: 'muted empty' }, ['None yet.']) : h('ul', { class: 'rows' }, picked.map((sheet) => h('li', { class: 'sheet' }, [
36
+ h('strong', {}, [sheet.title]), ' ', h('code', {}, [sheet.spreadsheetId]),
37
+ ]))),
38
+ ]));
39
+ }
40
+
41
+ async function refresh(bootstrap) {
42
+ picked = (await client.call({ action: 'list-picked', payload: {} })).spreadsheets;
43
+ render(bootstrap);
44
+ }
45
+
46
+ async function choose(bootstrap) {
47
+ waiting = true;
48
+ stopRequested = false;
49
+ status = '';
50
+ statusTone = 'info';
51
+ render(bootstrap);
52
+ try {
53
+ // The Choose button stays disabled while waiting; "Stop waiting" stays usable.
54
+ const chosen = await chooseSpreadsheet({ client, bootstrap, onStatus: (text) => { status = text; render(bootstrap); }, stopped: () => stopRequested });
55
+ if (chosen) status = selectedText({ spreadsheet: chosen });
56
+ else status ||= CANCELLED_TEXT;
57
+ } catch (error) {
58
+ status = failureText(error);
59
+ statusTone = 'error';
60
+ }
61
+ waiting = false;
62
+ // A failed refresh must still re-render: the last render showed Choose disabled (waiting).
63
+ await refresh(bootstrap).catch((error) => { status = failureText(error); statusTone = 'error'; render(bootstrap); });
64
+ }
65
+
66
+ /**
67
+ * Starts the panel in a frame: connects to the host and lists the chosen spreadsheets once the host's
68
+ * init arrives. Exported so a test can start a fresh panel (fresh state) in its own jsdom window; the
69
+ * last line starts the real one.
70
+ * @param {{ window: Window, document: Document }} required
71
+ */
72
+ export function initPickerPanel(required) {
73
+ client = connectToHost({ window: required.window });
74
+ app = required.document.getElementById('app');
75
+ status = '';
76
+ statusTone = 'info';
77
+ waiting = false;
78
+ stopRequested = false;
79
+ picked = [];
80
+ client.ready.then(load);
81
+ }
82
+
83
+ /** The first list; a failure the host calls retryable offers "Try again" (another list). */
84
+ function load() {
85
+ return refresh(client.bootstrap()).catch((error) => show(app, notice({ text: failureText(error), retry: error?.retryable === true ? load : undefined })));
86
+ }
87
+
88
+ initPickerPanel({ window, document });
@@ -0,0 +1,227 @@
1
+ // Verbatim copy of @jini-ai/agent-plugins dist/admin-surfaces/surface-client.js (Jini db1e99fb + host theme tokens + frame height).
2
+ // The surface is plain static files served without credentials, so it carries its own copy of the
3
+ // self-contained protocol client instead of importing the package; refresh it when the protocol changes.
4
+ /**
5
+ * @file The surface-side half of the admin-surface message protocol, for a plugin's own UI.
6
+ *
7
+ * Self-contained on purpose (no imports): a plugin's surface is plain static files, so it may import
8
+ * this module from the package or copy the compiled file next to its entry. The constants repeat
9
+ * `./protocol.ts`; the vertical-slice test proves the two still agree.
10
+ *
11
+ * Trust rules it applies: it accepts init only from its parent window (once), learns the host's
12
+ * origin from that init and posts only to that origin (never "*"), and accepts a response only from
13
+ * the parent, from that origin, for this mount, for a request it still has pending, in a valid shape.
14
+ *
15
+ * Theme: the host may send its design tokens with init and again in `theme` messages (`./theme.ts`).
16
+ * The client applies the `--jini-*` ones with a safe value as custom properties on the surface's root
17
+ * element (CSSOM `setProperty`, which a CSP without 'unsafe-inline' allows), plus `color-scheme` and
18
+ * `data-jini-scheme`, so the surface's own stylesheet can follow the admin's look and light/dark mode.
19
+ */
20
+ const PROTOCOL = 'jini.admin-surface';
21
+ const VERSION = 1;
22
+ const DEFAULT_TIMEOUT_MS = 30_000;
23
+ const THEME_TOKEN = /^--jini-[a-z0-9-]{1,60}$/;
24
+ const UNSAFE_THEME_VALUE = /url\(|image\(|image-set\(|[;{}<>\\@]/i;
25
+ const MAX_THEME_TOKENS = 100;
26
+ /** Rejection value of `call` when the host refused the action or the request timed out. */
27
+ export class AdminSurfaceCallError extends Error {
28
+ code;
29
+ retryable;
30
+ constructor(required) {
31
+ super(required.message);
32
+ this.name = 'AdminSurfaceCallError';
33
+ this.code = required.code;
34
+ this.retryable = required.retryable;
35
+ }
36
+ }
37
+ function isMessage(data, kind) {
38
+ if (typeof data !== 'object' || data === null)
39
+ return false;
40
+ const message = data;
41
+ return message.protocol === PROTOCOL && message.version === VERSION && message.kind === kind;
42
+ }
43
+ function isFailure(value) {
44
+ if (typeof value !== 'object' || value === null)
45
+ return false;
46
+ const error = value;
47
+ return typeof error.code === 'string' && typeof error.message === 'string' && typeof error.retryable === 'boolean';
48
+ }
49
+ /** The host's theme with every unknown name or unsafe value dropped; undefined when it is not a theme. */
50
+ function acceptedTheme(value) {
51
+ if (typeof value !== 'object' || value === null)
52
+ return undefined;
53
+ const { scheme, tokens } = value;
54
+ if ((scheme !== 'light' && scheme !== 'dark') || typeof tokens !== 'object' || tokens === null)
55
+ return undefined;
56
+ const safe = Object.entries(tokens).slice(0, MAX_THEME_TOKENS).filter(([name, token]) => {
57
+ if (!THEME_TOKEN.test(name) || typeof token !== 'string') return false;
58
+ return token.length > 0 && token.length <= 200 && !UNSAFE_THEME_VALUE.test(token);
59
+ });
60
+ return { scheme, tokens: Object.fromEntries(safe) };
61
+ }
62
+ /** The browser root when there is a document (none in a server-side test). */
63
+ function documentRoot() {
64
+ const doc = globalThis.document;
65
+ return doc?.documentElement ?? null;
66
+ }
67
+ /**
68
+ * The browser's size source: the body's height including what overflows it (an open toggletip), re-read
69
+ * on resize, DOM changes, hover and focus (hover/focus can show a CSS-only tooltip). Null without a DOM.
70
+ */
71
+ function documentSize() {
72
+ const scope = globalThis;
73
+ const body = scope.document?.body;
74
+ if (!body || !scope.ResizeObserver || !scope.MutationObserver)
75
+ return null;
76
+ const { ResizeObserver: Resize, MutationObserver: Mutation } = scope;
77
+ return (onHeight) => {
78
+ const measure = () => {
79
+ const style = getComputedStyle(body);
80
+ // scrollHeight is rounded to whole pixels; the box height keeps the fraction, which would
81
+ // otherwise leave a 1px inner scrollbar.
82
+ const content = Math.max(body.scrollHeight, body.getBoundingClientRect().height);
83
+ onHeight(Math.ceil(content + parseFloat(style.marginTop || '0') + parseFloat(style.marginBottom || '0')));
84
+ };
85
+ const resize = new Resize(measure);
86
+ const mutation = new Mutation(measure);
87
+ resize.observe(body);
88
+ mutation.observe(body, { subtree: true, childList: true, attributes: true, characterData: true });
89
+ body.addEventListener('mouseover', measure);
90
+ body.addEventListener('focusin', measure);
91
+ measure();
92
+ return () => { resize.disconnect(); mutation.disconnect(); body.removeEventListener('mouseover', measure); body.removeEventListener('focusin', measure); };
93
+ };
94
+ }
95
+ /** A response has `ok: true` with a result, or `ok: false` with a well-formed failure. */
96
+ function isWellFormedResponse(message) {
97
+ return message.ok === true ? Object.hasOwn(message, 'result') : message.ok === false && isFailure(message.error);
98
+ }
99
+ /**
100
+ * Connects a surface to its host. In a browser:
101
+ * `createAdminSurfaceClient({ parent: window.parent, listen: (h) => { addEventListener('message', h); return () => removeEventListener('message', h); } })`.
102
+ * @param optional.timeoutMs - How long `call` waits for a response before rejecting with `timeout`.
103
+ * @complexity O(1) per message.
104
+ */
105
+ export function createAdminSurfaceClient(required, optional = {}) {
106
+ const timeoutMs = optional.timeoutMs ?? DEFAULT_TIMEOUT_MS;
107
+ const nowMs = optional.nowMs ?? Date.now;
108
+ const pending = new Map();
109
+ let init;
110
+ let nextRequest = 0;
111
+ // The Promise executor runs synchronously, so `resolveReady` is assigned before anything can call it.
112
+ let resolveReady;
113
+ const ready = new Promise((resolve) => { resolveReady = resolve; });
114
+ const themeRoot = optional.themeRoot === undefined ? documentRoot() : optional.themeRoot;
115
+ let currentTheme;
116
+ const size = optional.size === undefined ? documentSize() : optional.size;
117
+ let stopSize;
118
+ let lastHeight = -1;
119
+ /** Tells the host this surface's height (only when it changed), so the frame fits its content. */
120
+ function reportHeight(height) {
121
+ if (init === undefined || !Number.isFinite(height) || height === lastHeight)
122
+ return;
123
+ lastHeight = height;
124
+ required.parent.postMessage({ protocol: PROTOCOL, version: VERSION, kind: 'resize', mountId: init.mountId, height }, init.origin);
125
+ }
126
+ /** Replaces the applied tokens: a token the new theme no longer sends falls back to the surface's CSS. */
127
+ function applyTheme(value) {
128
+ const next = acceptedTheme(value);
129
+ if (next === undefined)
130
+ return;
131
+ if (themeRoot !== null) {
132
+ for (const name of Object.keys(currentTheme?.tokens ?? {}))
133
+ if (!(name in next.tokens))
134
+ themeRoot.style.removeProperty(name);
135
+ for (const [name, token] of Object.entries(next.tokens))
136
+ themeRoot.style.setProperty(name, token);
137
+ themeRoot.style.setProperty('color-scheme', next.scheme);
138
+ themeRoot.setAttribute('data-jini-scheme', next.scheme);
139
+ }
140
+ currentTheme = next;
141
+ }
142
+ function onInit(event, data) {
143
+ if (init !== undefined || typeof data.mountId !== 'string')
144
+ return;
145
+ init = { mountId: data.mountId, origin: event.origin, bootstrap: data.bootstrap };
146
+ applyTheme(data.theme);
147
+ stopSize = size?.(reportHeight);
148
+ resolveReady();
149
+ }
150
+ function onResponse(event, data) {
151
+ if (event.origin !== init?.origin || data.mountId !== init.mountId || typeof data.requestId !== 'string')
152
+ return;
153
+ const waiting = pending.get(data.requestId);
154
+ if (waiting === undefined || !isWellFormedResponse(data))
155
+ return;
156
+ pending.delete(data.requestId);
157
+ clearTimeout(waiting.timer);
158
+ if (data.ok === true)
159
+ waiting.resolve(data.result);
160
+ else
161
+ waiting.reject(new AdminSurfaceCallError(data.error));
162
+ }
163
+ const unsubscribe = required.listen((event) => {
164
+ if (event.source !== required.parent)
165
+ return;
166
+ if (isMessage(event.data, 'init'))
167
+ onInit(event, event.data);
168
+ else if (isMessage(event.data, 'theme')) {
169
+ if (event.origin === init?.origin && event.data.mountId === init.mountId)
170
+ applyTheme(event.data.theme);
171
+ }
172
+ else if (isMessage(event.data, 'action-response'))
173
+ onResponse(event, event.data);
174
+ else {
175
+ // Ignore message kinds outside the admin-surface protocol.
176
+ }
177
+ });
178
+ /** Posts one request of `kind` to the host origin and waits for its `action-response`. */
179
+ async function send(kind, fields) {
180
+ if (init === undefined)
181
+ await ready;
182
+ const { mountId, origin } = init;
183
+ nextRequest += 1;
184
+ const requestId = `r${nextRequest}`;
185
+ return new Promise((resolve, reject) => {
186
+ const timer = setTimeout(() => {
187
+ pending.delete(requestId);
188
+ reject(new AdminSurfaceCallError({ code: 'timeout', message: 'The host did not answer in time. Try again.', retryable: true }));
189
+ }, timeoutMs);
190
+ pending.set(requestId, { resolve, reject, timer });
191
+ required.parent.postMessage({ protocol: PROTOCOL, version: VERSION, kind, mountId, requestId, ...fields }, origin);
192
+ });
193
+ }
194
+ /** Asks the host to run one declared action; resolves with its result. */
195
+ function call(input) {
196
+ return send('action-request', { action: input.action, payload: input.payload });
197
+ }
198
+ /** Asks the host to open `url` outside the sandbox; only https links on the surface's declared
199
+ * `linkOrigins` open, and the host decides how (new tab, system browser). Rejects with the host's refusal. */
200
+ async function openLink(input) {
201
+ await send('open-link', { url: input.url });
202
+ }
203
+ /** The bootstrap `data` the host sent with init, or `undefined` once it has expired (or none was sent). */
204
+ function bootstrap() {
205
+ const envelope = init?.bootstrap;
206
+ if (typeof envelope !== 'object' || envelope === null)
207
+ return undefined;
208
+ const { expiresAt, data } = envelope;
209
+ return typeof expiresAt === 'number' && nowMs() < expiresAt ? data : undefined;
210
+ }
211
+ /** The host's theme as applied, or `undefined` when the host sent none. */
212
+ function theme() {
213
+ return currentTheme;
214
+ }
215
+ /** Stops listening and rejects every pending call. */
216
+ function close() {
217
+ unsubscribe();
218
+ stopSize?.();
219
+ for (const [requestId, waiting] of pending) {
220
+ clearTimeout(waiting.timer);
221
+ waiting.reject(new AdminSurfaceCallError({ code: 'closed', message: 'The surface was closed before the host answered.', retryable: false }));
222
+ pending.delete(requestId);
223
+ }
224
+ }
225
+ return { ready, call, openLink, bootstrap, theme, close };
226
+ }
227
+ //# sourceMappingURL=surface-client.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
3
+ "mcpServers": {
4
+ "higgsfield": {
5
+ "type": "streamable-http",
6
+ "url": "https://mcp.higgsfield.ai/mcp"
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,109 @@
1
+ {
2
+ "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
+ "name": "higgsfield-media",
4
+ "version": "1.1.0",
5
+ "description": "Generates images or video through Higgsfield's hosted MCP server, follows asynchronous jobs, then imports the returned asset into this site's Media library. Connect with agent_plugin_connect and the person's browser sign-in, never an API key. Discover this account's current tools, schemas and model entitlements; the worked model and plan examples are historical, not guarantees. Covers operator write grants, polling without sync:true, connection recovery and media_import_from_url.",
6
+ "author": "Tovu",
7
+ "license": "Apache-2.0",
8
+ "keywords": [
9
+ "higgsfield",
10
+ "image",
11
+ "image-generation",
12
+ "generate",
13
+ "media",
14
+ "media-library",
15
+ "import",
16
+ "external-mcp",
17
+ "federation",
18
+ "mcp",
19
+ "oauth",
20
+ "async",
21
+ "job-status",
22
+ "polling",
23
+ "cdn",
24
+ "photo",
25
+ "photograph",
26
+ "picture",
27
+ "illustration",
28
+ "art",
29
+ "artwork",
30
+ "visual",
31
+ "graphic",
32
+ "connect",
33
+ "connection",
34
+ "sign-in",
35
+ "login",
36
+ "authorize",
37
+ "authentication",
38
+ "credential",
39
+ "setup",
40
+ "onboarding",
41
+ "getting-started",
42
+ "api-key",
43
+ "text-to-image",
44
+ "higgsfield-media",
45
+ "media:image",
46
+ "media:video"
47
+ ],
48
+ "extensions": {
49
+ "tovu": {
50
+ "vendorAliases": ["higgsfield"],
51
+ "summary": "Makes AI images with Higgsfield and saves them to your Media library. You sign in to Higgsfield once in your browser.",
52
+ "mcpServers": {
53
+ "higgsfield": {
54
+ "tovuAuthMode": "oauth",
55
+ "tovuDefaultTools": {
56
+ "allow": [
57
+ "generate_image",
58
+ "generate_video",
59
+ "models_explore",
60
+ "job_status",
61
+ "jobs_wait",
62
+ "show_generations",
63
+ "reveal_generation",
64
+ "show_generation_by_ids"
65
+ ],
66
+ "write": [
67
+ "generate_image",
68
+ "generate_video",
69
+ "reveal_generation"
70
+ ],
71
+ "read": [
72
+ "models_explore",
73
+ "job_status",
74
+ "jobs_wait",
75
+ "show_generations",
76
+ "show_generation_by_ids"
77
+ ]
78
+ }
79
+ }
80
+ },
81
+ "id": "tovu/higgsfield-media",
82
+ "provides": [
83
+ "media:image",
84
+ "media:video"
85
+ ],
86
+ "credentialCards": [
87
+ {
88
+ "target": "higgsfield-media",
89
+ "title": "Connect Higgsfield",
90
+ "label": "Higgsfield account",
91
+ "category": "media",
92
+ "intro": "Connect Higgsfield to generate images or video and save the finished files in this site’s Media library.",
93
+ "usedFor": [
94
+ "Checks available image and video models and generation progress",
95
+ "Generates images and videos using your account's credits"
96
+ ],
97
+ "keyHint": "Browser sign-in only; this connection does not accept a pasted API key.",
98
+ "saveKind": "oauth",
99
+ "permissions": "Signing in turns on model discovery, job status and generation. Generation uses the models and credits available on your account. You can turn any tool off later in the connection settings. Review the permissions on the sign-in page.",
100
+ "baseUrl": "https://mcp.higgsfield.ai"
101
+ }
102
+ ],
103
+ "toolKeywords": {
104
+ "external_mcp_list": "higgsfield",
105
+ "external_mcp_save": "higgsfield"
106
+ }
107
+ }
108
+ }
109
+ }