@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.
- package/CHANGELOG.md +29 -1
- package/NOTICE +14 -0
- package/README.md +35 -14
- package/charts-echarts/plugin.json +39 -0
- package/charts-echarts/skills/charts-echarts/SKILL.md +110 -0
- package/charts-recharts/plugin.json +32 -0
- package/charts-recharts/skills/charts-recharts/SKILL.md +108 -0
- package/cloudflare/NOTES.md +16 -0
- package/cloudflare/form-destinations/cloudflare-d1.mjs +9 -0
- package/cloudflare/lib/api.mjs +96 -0
- package/cloudflare/lib/assets.mjs +65 -0
- package/cloudflare/lib/dns.mjs +32 -0
- package/cloudflare/lib/domain-inventory.mjs +61 -0
- package/cloudflare/lib/domain-status.mjs +48 -0
- package/cloudflare/lib/domains.mjs +95 -0
- package/cloudflare/lib/forms.mjs +254 -0
- package/cloudflare/lib/pages-upload.mjs +51 -0
- package/cloudflare/lib/target.mjs +165 -0
- package/cloudflare/lib/workers-upload.mjs +48 -0
- package/cloudflare/lib/zones.mjs +25 -0
- package/cloudflare/mcp.json +4 -0
- package/cloudflare/plugin.json +96 -0
- package/cloudflare/skills/cloudflare/SKILL.md +57 -0
- package/cloudflare/skills/cloudflare/references/domains.md +31 -0
- package/cloudflare/skills/cloudflare/references/pages.md +18 -0
- package/cloudflare/skills/cloudflare/references/permissions.md +29 -0
- package/cloudflare/skills/cloudflare/references/troubleshooting.md +20 -0
- package/cloudflare/skills/cloudflare/references/workers.md +42 -0
- package/cloudflare/targets/cloudflare-pages.mjs +12 -0
- package/cloudflare/targets/cloudflare-workers.mjs +17 -0
- package/cloudflare/tovu-consent.json +6 -0
- package/cloudflare/tovu-deploy-targets.json +630 -0
- package/cloudflare/tovu-form-destinations.json +12 -0
- package/cloudflare/worker/forms-receiver.mjs +249 -0
- package/composio/mcp.json +10 -0
- package/composio/plugin.json +68 -0
- package/composio/skills/composio/SKILL.md +85 -0
- package/dist/core/identity-namespace.d.ts +12 -0
- package/dist/core/identity-namespace.js +20 -0
- package/dist/folder-zip-node.js +29 -7
- package/dist/lifecycle/bundled-digests.js +4 -3
- package/dist/lifecycle/install.d.ts +7 -0
- package/dist/lifecycle/install.js +45 -3
- package/dist/lifecycle/manifest.d.ts +2 -2
- package/dist/lifecycle/manifest.js +5 -3
- package/dist/lifecycle/node.js +2 -0
- package/dist/lifecycle/persistent-state.js +1 -1
- package/dist/lifecycle/ports.d.ts +8 -0
- package/dist/lifecycle/seed-bundled.js +4 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +3 -2
- package/fly/deploy-configs/fly.mjs +115 -0
- package/fly/deploy-ops/fly.mjs +102 -0
- package/fly/mcp.json +4 -0
- package/fly/plugin.json +81 -0
- package/fly/scripts/render-deploy-workflow.mjs +150 -0
- package/fly/skills/fly/SKILL.md +43 -0
- package/fly/skills/fly/references/fly-deploy.template.yml +117 -0
- package/fly/skills/fly/references/fly.template.toml +81 -0
- package/fly/tovu-credential-schemes.json +11 -0
- package/fly/tovu-deploy-configs.json +21 -0
- package/fly/tovu-deploy-ops.json +14 -0
- package/github/deploy-ops/github-actions.mjs +164 -0
- package/github/deployments/github.mjs +415 -0
- package/github/mcp.json +4 -0
- package/github/plugin.json +105 -0
- package/github/skills/github/SKILL.md +249 -0
- package/github/skills/github/references/actions.md +224 -0
- package/github/skills/github/references/auth-and-tokens.md +140 -0
- package/github/skills/github/references/repo-files.md +135 -0
- package/github/skills/github/references/site-backup.md +159 -0
- package/github/skills/github/references/source-control.md +123 -0
- package/github/source-control/backup-push.mjs +242 -0
- package/github/source-control/commit-site.mjs +459 -0
- package/github/source-control/github.mjs +93 -0
- package/github/source-control/write-files.mjs +468 -0
- package/github/tovu-deploy-ops.json +13 -0
- package/github/tovu-source-control.json +117 -0
- package/github-pages/NOTES.md +19 -0
- package/github-pages/mcp.json +4 -0
- package/github-pages/plugin.json +48 -0
- package/github-pages/skills/github-pages/SKILL.md +44 -0
- package/github-pages/targets/github-pages.mjs +543 -0
- package/github-pages/tovu-deploy-targets.json +327 -0
- package/google-login/exports/google-g.svg +1 -0
- package/google-login/exports/sign-in-button.css +68 -0
- package/google-login/exports/sign-in-button.html +4 -0
- package/google-login/mcp.json +4 -0
- package/google-login/plugin.json +59 -0
- package/google-login/skills/google-login/SKILL.md +96 -0
- package/google-login/tovu-login-providers.json +21 -0
- package/google-workspace/README.md +132 -0
- package/google-workspace/form-destinations/README.md +87 -0
- package/google-workspace/form-destinations/dedupe.mjs +43 -0
- package/google-workspace/form-destinations/delivery-result.mjs +69 -0
- package/google-workspace/form-destinations/mapping.mjs +152 -0
- package/google-workspace/form-destinations/sheets.mjs +202 -0
- package/google-workspace/lib/auth.mjs +81 -0
- package/google-workspace/lib/drive-files.mjs +54 -0
- package/google-workspace/lib/form-destination-settings.mjs +72 -0
- package/google-workspace/lib/http.mjs +168 -0
- package/google-workspace/lib/picker-flow.mjs +171 -0
- package/google-workspace/lib/picker-selection.mjs +107 -0
- package/google-workspace/lib/product-state.mjs +93 -0
- package/google-workspace/lib/request-policy.mjs +163 -0
- package/google-workspace/lib/sheets-a1.mjs +79 -0
- package/google-workspace/lib/sheets-admin-csv.mjs +82 -0
- package/google-workspace/lib/sheets-admin-request-kinds.mjs +183 -0
- package/google-workspace/lib/sheets-client.mjs +286 -0
- package/google-workspace/lib/surface-actions.mjs +107 -0
- package/google-workspace/mcp.json +4 -0
- package/google-workspace/plugin.json +77 -0
- package/google-workspace/products.json +12 -0
- package/google-workspace/skills/google-workspace/SKILL.md +82 -0
- package/google-workspace/skills/google-workspace/references/connection.md +102 -0
- package/google-workspace/skills/google-workspace/references/form-delivery.md +50 -0
- package/google-workspace/skills/google-workspace/references/sheets.md +149 -0
- package/google-workspace/skills/google-workspace/references/troubleshooting.md +31 -0
- package/google-workspace/tools/account-info.mjs +18 -0
- package/google-workspace/tools/sheets/README.md +54 -0
- package/google-workspace/tools/sheets/copy-tab.mjs +23 -0
- package/google-workspace/tools/sheets/copy.mjs +23 -0
- package/google-workspace/tools/sheets/create.mjs +35 -0
- package/google-workspace/tools/sheets/find-replace.mjs +38 -0
- package/google-workspace/tools/sheets/find.mjs +52 -0
- package/google-workspace/tools/sheets/get-by-data-filter.mjs +23 -0
- package/google-workspace/tools/sheets/get.mjs +22 -0
- package/google-workspace/tools/sheets/values-append.mjs +27 -0
- package/google-workspace/tools/sheets/values-batch-clear.mjs +20 -0
- package/google-workspace/tools/sheets/values-batch-get.mjs +20 -0
- package/google-workspace/tools/sheets/values-batch-update.mjs +29 -0
- package/google-workspace/tools/sheets/values-by-data-filter.mjs +62 -0
- package/google-workspace/tools/sheets/values-clear.mjs +20 -0
- package/google-workspace/tools/sheets/values-get.mjs +21 -0
- package/google-workspace/tools/sheets/values-update.mjs +26 -0
- package/google-workspace/tools/sheets-admin/README.md +45 -0
- package/google-workspace/tools/sheets-admin/basic-filter.mjs +32 -0
- package/google-workspace/tools/sheets-admin/batch-update.mjs +25 -0
- package/google-workspace/tools/sheets-admin/developer-metadata.mjs +28 -0
- package/google-workspace/tools/sheets-admin/export-csv.mjs +67 -0
- package/google-workspace/tools/sheets-admin/filter-views.mjs +38 -0
- package/google-workspace/tools/sheets-admin/format.mjs +70 -0
- package/google-workspace/tools/sheets-admin/named-ranges.mjs +42 -0
- package/google-workspace/tools/sheets-admin/open-existing.mjs +34 -0
- package/google-workspace/tools/sheets-admin/protected-ranges-get.mjs +18 -0
- package/google-workspace/tools/sheets-admin/sort.mjs +18 -0
- package/google-workspace/tools/sheets-admin/tab-modify.mjs +36 -0
- package/google-workspace/tovu-admin-surfaces.json +63 -0
- package/google-workspace/tovu-agent-tools.json +1178 -0
- package/google-workspace/tovu-connections.json +35 -0
- package/google-workspace/tovu-form-destinations.json +10 -0
- package/google-workspace/ui/README.md +73 -0
- package/google-workspace/ui/connection-panel.handler.mjs +167 -0
- package/google-workspace/ui/connection-panel.html +17 -0
- package/google-workspace/ui/connection-panel.mjs +189 -0
- package/google-workspace/ui/connection-view.mjs +120 -0
- package/google-workspace/ui/destination-panel.handler.mjs +106 -0
- package/google-workspace/ui/destination-panel.html +17 -0
- package/google-workspace/ui/destination-panel.mjs +150 -0
- package/google-workspace/ui/dom.mjs +140 -0
- package/google-workspace/ui/panel.css +170 -0
- package/google-workspace/ui/picker-flow.mjs +87 -0
- package/google-workspace/ui/picker-view.mjs +44 -0
- package/google-workspace/ui/picker.handler.mjs +56 -0
- package/google-workspace/ui/picker.html +17 -0
- package/google-workspace/ui/picker.mjs +88 -0
- package/google-workspace/ui/surface-client.mjs +227 -0
- package/higgsfield-media/mcp.json +9 -0
- package/higgsfield-media/plugin.json +109 -0
- package/higgsfield-media/skills/higgsfield-media/SKILL.md +325 -0
- package/higgsfield-media/skills/higgsfield-media/references/failure-modes.md +116 -0
- package/higgsfield-media/skills/higgsfield-media/references/models-and-plan-gates.md +142 -0
- package/jev/mcp.json +4 -0
- package/jev/plugin.json +44 -0
- package/jev/skills/jev/SKILL.md +308 -0
- package/jev/skills/jev/references/cookbooks.md +197 -0
- package/jev/skills/jev/references/jaggedness.md +135 -0
- package/jev/skills/jev/references/javascript-sdk.md +146 -0
- package/jev/skills/jev/references/patterns.md +123 -0
- package/jev/skills/jev/references/primitives-reference.md +187 -0
- package/langfuse/plugin.json +22 -0
- package/meilisearch/mcp.json +4 -0
- package/meilisearch/plugin.json +54 -0
- package/meilisearch/providers/meilisearch.mjs +651 -0
- package/meilisearch/skills/meilisearch/SKILL.md +43 -0
- package/meilisearch/tovu-search-providers.json +10 -0
- package/netlify/NOTES.md +13 -0
- package/netlify/mcp.json +4 -0
- package/netlify/plugin.json +47 -0
- package/netlify/skills/netlify/SKILL.md +39 -0
- package/netlify/targets/netlify.mjs +384 -0
- package/netlify/tovu-consent.json +6 -0
- package/netlify/tovu-deploy-targets.json +163 -0
- package/opentelemetry/mcp.json +4 -0
- package/opentelemetry/plugin.json +52 -0
- package/opentelemetry/skills/opentelemetry/SKILL.md +39 -0
- package/package.json +161 -6
- package/posthog/mcp.json +4 -0
- package/posthog/plugin.json +46 -0
- package/posthog/skills/posthog/SKILL.md +36 -0
- package/railway/deploy-configs/railway.mjs +135 -0
- package/railway/deploy-ops/railway.mjs +167 -0
- package/railway/mcp.json +4 -0
- package/railway/plugin.json +45 -0
- package/railway/skills/railway/SKILL.md +66 -0
- package/railway/tovu-deploy-configs.json +16 -0
- package/railway/tovu-deploy-ops.json +11 -0
- package/render/deploy-configs/render.mjs +141 -0
- package/render/deploy-ops/render.mjs +152 -0
- package/render/mcp.json +4 -0
- package/render/plugin.json +47 -0
- package/render/skills/render/SKILL.md +63 -0
- package/render/tovu-deploy-configs.json +16 -0
- package/render/tovu-deploy-ops.json +11 -0
- package/resend/NOTES.md +52 -0
- package/resend/adapters/resend.mjs +161 -0
- package/resend/lib/domains.mjs +137 -0
- package/resend/lib/errors.mjs +25 -0
- package/resend/lib/feedback.mjs +118 -0
- package/resend/mcp.json +13 -0
- package/resend/plugin.json +192 -0
- package/resend/skills/resend/SKILL.md +107 -0
- package/resend/tovu-mail-adapters.json +22 -0
- package/s3-compatible/NOTES.md +11 -0
- package/s3-compatible/mcp.json +4 -0
- package/s3-compatible/object-stores/s3-compatible.mjs +287 -0
- package/s3-compatible/plugin.json +56 -0
- package/s3-compatible/skills/s3-compatible/SKILL.md +39 -0
- package/s3-compatible/targets/s3-compatible.mjs +1054 -0
- package/s3-compatible/tovu-deploy-targets.json +432 -0
- package/s3-compatible/tovu-object-stores.json +29 -0
- package/sentry/mcp.json +4 -0
- package/sentry/plugin.json +49 -0
- package/sentry/skills/sentry/SKILL.md +34 -0
- package/shopify/mcp.json +4 -0
- package/shopify/plugin.json +22 -0
- package/shopify/skills/shopify/SKILL.md +103 -0
- package/shopify/skills/shopify/references/catalog-sync.md +74 -0
- package/shopify/skills/shopify/references/domains.md +26 -0
- package/shopify/skills/shopify/references/live-storefront.md +34 -0
- package/shopify/skills/shopify/references/storefront-snippets.md +21 -0
- package/site-compliance/mcp.json +4 -0
- package/site-compliance/plugin.json +34 -0
- package/site-compliance/skills/site-compliance/SKILL.md +220 -0
- package/site-compliance/skills/site-compliance/references/ccpa-cpra.md +115 -0
- package/site-compliance/skills/site-compliance/references/gdpr-eu-uk.md +143 -0
- package/site-compliance/skills/site-compliance/references/wcag-2-2.md +129 -0
- package/site-import/mcp.json +4 -0
- package/site-import/plugin.json +41 -0
- package/site-import/skills/site-import/SKILL.md +232 -0
- package/site-import/skills/site-import/references/content-mapping.md +166 -0
- package/site-import/skills/site-import/references/discovery-and-classification.md +117 -0
- package/site-import/skills/site-import/references/theme-extraction.md +34 -0
- package/slide-design/com.anthropic.claude-code/hooks/hooks.json +4 -0
- package/slide-design/mcp.json +4 -0
- package/slide-design/plugin.json +26 -0
- package/slide-design/skills/slide-design/SKILL.md +103 -0
- package/slide-design/skills/slide-design/references/charts-and-exhibits.md +100 -0
- package/slide-design/skills/slide-design/references/html-deck.md +175 -0
- package/slide-design/skills/slide-design/references/layout-and-style.md +131 -0
- package/slide-design/skills/slide-design/references/review.md +93 -0
- package/slide-design/skills/slide-design/references/storyline.md +118 -0
- package/stripe/plugin.json +22 -0
- package/supabase/mcp.json +26 -0
- package/supabase/plugin.json +78 -0
- package/supabase/skills/supabase/SKILL.md +169 -0
- package/supabase/skills/supabase/references/failure-modes.md +101 -0
- package/ui-ux-design/CHANGELOG.md +28 -0
- package/ui-ux-design/LICENSE +201 -0
- package/ui-ux-design/NOTICE +14 -0
- package/ui-ux-design/README.md +62 -0
- package/ui-ux-design/com.anthropic.claude-code/hooks/hooks.json +4 -0
- package/ui-ux-design/mcp.json +4 -0
- package/ui-ux-design/plugin.json +26 -0
- package/ui-ux-design/skills/frontend-accessibility/SKILL.md +60 -0
- package/ui-ux-design/skills/gstack-design/SKILL.md +53 -0
- package/ui-ux-design/skills/gstack-design/references/consultation.md +30 -0
- package/ui-ux-design/skills/gstack-design/references/html.md +29 -0
- package/ui-ux-design/skills/gstack-design/references/review.md +28 -0
- package/ui-ux-design/skills/gstack-design/references/shotgun.md +33 -0
- package/ui-ux-design/skills/gstack-design/references/upstream-notes.md +23 -0
- package/ui-ux-design/skills/interface-design/ORIGINAL.md +16 -0
- package/ui-ux-design/skills/interface-design/SKILL.md +41 -0
- package/ui-ux-design/skills/interface-design/references/commands/audit.md +40 -0
- package/ui-ux-design/skills/interface-design/references/commands/critique.md +21 -0
- package/ui-ux-design/skills/interface-design/references/commands/extract.md +34 -0
- package/ui-ux-design/skills/interface-design/references/commands/init.md +31 -0
- package/ui-ux-design/skills/interface-design/references/commands/status.md +22 -0
- package/ui-ux-design/skills/interface-design/references/examples/system-precision.md +29 -0
- package/ui-ux-design/skills/interface-design/references/examples/system-warmth.md +29 -0
- package/ui-ux-design/skills/interface-design/references/system-template.md +57 -0
- package/ui-ux-design/skills/shadcn-ui/README.md +244 -0
- package/ui-ux-design/skills/shadcn-ui/SKILL.md +333 -0
- package/ui-ux-design/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
- package/ui-ux-design/skills/shadcn-ui/examples/data-table.tsx +313 -0
- package/ui-ux-design/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
- package/ui-ux-design/skills/shadcn-ui/resources/component-catalog.md +481 -0
- package/ui-ux-design/skills/shadcn-ui/resources/customization-guide.md +516 -0
- package/ui-ux-design/skills/shadcn-ui/resources/migration-guide.md +463 -0
- package/ui-ux-design/skills/shadcn-ui/resources/setup-guide.md +412 -0
- package/ui-ux-design/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
- package/ui-ux-design/skills/ui-ux-design/SKILL.md +171 -0
- package/ui-ux-design/skills/ui-ux-design/references/brand-and-voice.md +78 -0
- package/ui-ux-design/skills/ui-ux-design/references/chrome-mobile-test-matrix.md +137 -0
- package/ui-ux-design/skills/ui-ux-design/references/components-and-states.md +112 -0
- package/ui-ux-design/skills/ui-ux-design/references/delight-and-motion.md +54 -0
- package/ui-ux-design/skills/ui-ux-design/references/foundations.md +145 -0
- package/ui-ux-design/skills/ui-ux-design/references/inclusive-ai-imagery.md +57 -0
- package/ui-ux-design/skills/ui-ux-design/references/kole-jain-uiux-concepts.md +104 -0
- package/ui-ux-design/skills/ui-ux-design/references/kuinetic-reference.md +184 -0
- package/ui-ux-design/skills/ui-ux-design/references/openai-frontend-skill.md +139 -0
- package/ui-ux-design/skills/ui-ux-design/references/premium-ui.md +55 -0
- package/ui-ux-design/skills/ui-ux-design/references/research-and-validation.md +88 -0
- package/ui-ux-design/skills/ui-ux-design/references/sam-crawford-premium-websites.md +103 -0
- package/ui-ux-design/skills/ui-ux-design/references/self-made-web-designer-core-skills.md +119 -0
- package/ui-ux-design/skills/ui-ux-design/references/ui-animation.md +173 -0
- package/ui-ux-design/skills/ui-ux-design/references/ui-loop.md +234 -0
- package/ui-ux-design/skills/ui-ux-design/references/visual-storytelling.md +54 -0
- package/ui-ux-design/skills/vercel-web-design-guidelines/SKILL.md +39 -0
- package/ui-ux-design/skills/web-compliance/SKILL.md +134 -0
- package/ui-ux-design/skills/web-compliance/references/audit-evidence.md +59 -0
- package/vercel/NOTES.md +23 -0
- package/vercel/mcp.json +4 -0
- package/vercel/object-stores/vercel-blob.mjs +147 -0
- package/vercel/plugin.json +47 -0
- package/vercel/skills/vercel/SKILL.md +48 -0
- package/vercel/targets/vercel.mjs +586 -0
- package/vercel/tovu-consent.json +6 -0
- package/vercel/tovu-deploy-targets.json +219 -0
- package/vercel/tovu-object-stores.json +10 -0
- package/wordpress/NOTES.md +33 -0
- package/wordpress/plugin.json +30 -0
- package/wordpress/skills/wordpress/SKILL.md +43 -0
- package/wordpress/skills/wordpress/references/free-site-redirect.md +46 -0
- package/wordpress/skills/wordpress/references/subdomain.md +66 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.19
|
|
4
|
+
|
|
5
|
+
- A plugin's state lock left by a killed boot no longer keeps that plugin off on every later boot after the machine's hostname spelling changed (macOS `.lan`/`.local`): the node process port records the kernel boot (`bootIdentity`), so the platform lock can prove the holder was this machine's and reclaim it once its pid is dead.
|
|
6
|
+
- Tests: the hosting plugins' identical `vitest-compat.mjs` (3 copies) and `fake-deploy-host-kit.mjs` (4 copies) are one shared test-only copy each under `packages/agent-plugins/__tests__/` (never published). Cloudflare's doubled DNS-marker and Pages-config comments are merged.
|
|
7
|
+
- Google Workspace: a Sheets `batchUpdate` with more than one request is never replayed after an unknown outcome. Each request was judged replay-safe alone, but Google applies them in order, so a retry of "copy A1 to B1, then set A1" changed B1.
|
|
8
|
+
- Security: reinstalling a plugin whose digest directory already exists compares that directory's bytes with the verified archive before reusing it. A same-build reseed used to keep an operator-edited bundled revision selected as shipped code; a modified copy is now kept aside under a `.modified-*` name (never indexed or selected) and the verified bytes are reinstalled.
|
|
9
|
+
- Railway's deploy-config generator refuses an `env` value containing a newline or control character (C0, DEL, C1) with an error naming the variable. Before, it printed the raw LF or NUL into the copy-paste `railway variables set` command. The error never echoes the value.
|
|
10
|
+
- Add the portable Shopify catalog and checkout skill under `@jini-ai/agent-plugins/shopify`; host collection tools, markup and domain routing stay in the host overlay.
|
|
11
|
+
- Pin Fly's portable template safety settings: operator placeholders, secret-free app configuration, single-machine defaults, serialized deploys and repository-secret token wiring.
|
|
12
|
+
- `charts-recharts` and `charts-echarts` declare `provides: ["chart"]` (the kind their provider draws), so capability lookup and automatic-use ranking find the chart faces.
|
|
13
|
+
- Plugin-folder suites (`<id>/**/__tests__/*.test.mjs`) are node:test files: the root `test:agent-plugins:content` script runs them and this package's vitest config no longer collects them. Shipped plugin `.mjs` modules are under the root `complexity:strict` gate.
|
|
14
|
+
- Every Jini agent plugin now ships in this package as a subpath: `@jini-ai/agent-plugins/<id>`
|
|
15
|
+
resolves to `<id>/plugin.json` and `@jini-ai/agent-plugins/<id>/*` to its files (ui-ux-design,
|
|
16
|
+
slide-design, the hosting, mail, search, login, workspace, media and chart faces, and the
|
|
17
|
+
posthog/sentry/opentelemetry setup faces). There are no per-plugin `@jini-ai/plugin-*` packages
|
|
18
|
+
for agent plugins; `__tests__/` folders are not published.
|
|
19
|
+
- Bundled seeding no longer prunes other revisions of a plugin. Reseeding kept deleting an
|
|
20
|
+
operator-edited, uploaded or older bundled same-name copy; every copy now survives and the
|
|
21
|
+
bundled-digests ledger selects the build's revision. `installAgentPlugin` takes
|
|
22
|
+
`{ pruneSupersededRevisions: false }`; explicit install, update and replace keep pruning.
|
|
23
|
+
- `zipCustomizedAgentPluginFolder` gives the fork `local/<name>` under every host key that
|
|
24
|
+
declared an id (and under the requesting host's key even when none existed), and removes
|
|
25
|
+
`renamedFrom` and `supersedes` from every key, not only the requesting host's.
|
|
26
|
+
- README documents the extension-namespace policy: one `extensions` key per host.
|
|
27
|
+
- slide-design review checklist matches the chart guide: a compact legend is fine when direct
|
|
28
|
+
labels would collide, and only magnitude bars and columns must start at zero (waterfall change
|
|
29
|
+
bars float).
|
|
30
|
+
|
|
3
31
|
## 0.6.17
|
|
4
32
|
|
|
5
33
|
### Patch Changes
|
|
@@ -18,7 +46,7 @@
|
|
|
18
46
|
|
|
19
47
|
- Breaking package-surface change for the fixed 0.6.15 release: remove the bundled
|
|
20
48
|
ui-ux-design files and wildcard export from @jini-ai/agent-plugins. Hosts must
|
|
21
|
-
|
|
49
|
+
use @jini-ai/agent-plugins/ui-ux-design to resolve its plugin.json
|
|
22
50
|
export, stage that payload and seed through the existing Agent Plugin lifecycle.
|
|
23
51
|
The lifecycle implementation remains in @jini-ai/agent-plugins. Inner plugin
|
|
24
52
|
version 0.4.6, deterministic packing, digest selection and operator activation
|
package/NOTICE
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Jini UI/UX Design Agent Plugin
|
|
2
|
+
Copyright 2026 Leona Burime
|
|
3
|
+
|
|
4
|
+
Portable skill sources remain at packages/agent-plugins/ui-ux-design.
|
|
5
|
+
Originally assembled from AI-Dev-Shop's Web Design skill trees; the ADS-specific
|
|
6
|
+
agent persona and routing implementation are not included.
|
|
7
|
+
|
|
8
|
+
Source notes and attribution are retained in the generated skills tree, including
|
|
9
|
+
skills/interface-design/ORIGINAL.md (Dammyjay93/interface-design) and
|
|
10
|
+
skills/gstack-design/references/upstream-notes.md (garrytan/gstack).
|
|
11
|
+
The skill references retain their original source links and attribution.
|
|
12
|
+
|
|
13
|
+
This distribution does not include the provider-plugin aggregate or its Open SaaS
|
|
14
|
+
Stripe adaptations. See LICENSE for the Apache License, Version 2.0.
|
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ decides its own install mechanism, permission model, and sandboxing.
|
|
|
29
29
|
|
|
30
30
|
## What a plugin looks like on disk
|
|
31
31
|
|
|
32
|
-
A plugin is a directory. Example (`@jini-ai/
|
|
32
|
+
A plugin is a directory. Example (this package publishes it as `@jini-ai/agent-plugins/ui-ux-design`):
|
|
33
33
|
|
|
34
34
|
```
|
|
35
35
|
ui-ux-design/
|
|
@@ -150,19 +150,22 @@ package supports. Do not confuse the two — "agent plugins" (this package, a
|
|
|
150
150
|
public spec) and "Jini plugins" (`@jini-ai/plugins`'s `./host`, Jini's own unbuilt format) share
|
|
151
151
|
the word "plugin" and nothing else.
|
|
152
152
|
|
|
153
|
-
##
|
|
153
|
+
## Plugins as subpaths
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
Every Jini agent plugin ships inside this package as its own folder and subpath:
|
|
156
|
+
`@jini-ai/agent-plugins/<id>` resolves to `<id>/plugin.json`, and `@jini-ai/agent-plugins/<id>/*`
|
|
157
|
+
reaches any file in that folder. Resolve the manifest (`require.resolve` or
|
|
158
|
+
`import.meta.resolve('@jini-ai/agent-plugins/vercel')`); its containing directory holds the
|
|
159
|
+
portable assets. Stage that directory beneath the host's bundled-source root, then call the existing
|
|
159
160
|
`seedBundledAgentPlugins({ layout, workspaceId, sourceRoot })`. Read contributions through trusted
|
|
160
161
|
installed-package readers. Seeding preserves digest selection and operator activation decisions.
|
|
161
162
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
163
|
+
A plugin folder imports nothing from a sibling plugin folder (`scripts/check-agent-plugin-content.ts`
|
|
164
|
+
and the packed-isolation gate refuse it), so resolving one subpath never loads another plugin's
|
|
165
|
+
files. Plugin folders sit outside `src/`: their `.tsx` examples are illustrative skill content and
|
|
166
|
+
must not enter the TypeScript compile root. `__tests__/` folders are not published. Provider
|
|
167
|
+
runtime code (charts, analytics, observability, payments) is not here; it ships as provider
|
|
168
|
+
plugins (`@jini-ai/provider-plugins/<provider>/*`).
|
|
166
169
|
|
|
167
170
|
## Scripts
|
|
168
171
|
|
|
@@ -177,10 +180,11 @@ translated to Vitest registration, preserving per-test timeouts and platform ski
|
|
|
177
180
|
|
|
178
181
|
## Adding another plugin
|
|
179
182
|
|
|
180
|
-
Create
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
183
|
+
Create `packages/agent-plugins/<id>/` with its `plugin.json` (name `<id>`), add its row to
|
|
184
|
+
`scripts/lib/agent-plugin-packages.ts`, and add `./<id>`, `./<id>/*` (exports and `jini.entries`)
|
|
185
|
+
and `<id>` (`files`) here. Shipped `.mjs` may import only `node:*`, same-folder files and
|
|
186
|
+
`@jini-ai/*` contracts; tests live in `<id>/**/__tests__/*.test.mjs` (node:test). Validate the
|
|
187
|
+
manifest and test installation through this package's existing lifecycle.
|
|
184
188
|
|
|
185
189
|
## Lifecycle API and ports
|
|
186
190
|
|
|
@@ -262,6 +266,23 @@ For a host that needs only activation, use `createAgentPluginActivations` with
|
|
|
262
266
|
ports. Retain one returned object for the process lifetime. See `API.md` for the
|
|
263
267
|
object-shaped reads, filters and optional legacy metadata reader.
|
|
264
268
|
|
|
269
|
+
### Extension namespaces
|
|
270
|
+
|
|
271
|
+
`extensions` keys in `plugin.json` are **host keys**. A plugin declares one entry per host it
|
|
272
|
+
supports, for example `"tovu": { "id": "tovu/stock" }` beside `"open-marketing": { "id": "tovu/stock" }`.
|
|
273
|
+
|
|
274
|
+
- A host reads only its own key, the `extensionNamespace` it passes to the lifecycle.
|
|
275
|
+
- Without a host namespace, exported parsers and validators read `jini`, falling back to the legacy
|
|
276
|
+
`tovu` key only when `jini` is absent. A present malformed `jini` entry is refused, never bypassed.
|
|
277
|
+
Passing a namespace explicitly reads only that key, with no fallback to another host.
|
|
278
|
+
- Publisher ids stay as declared. There is no alias between keys and no content rewrite.
|
|
279
|
+
- A customized fork (`zipCustomizedAgentPluginFolder`) gets `local/<name>` under every host key
|
|
280
|
+
that declared an id, and loses every `renamedFrom` and `supersedes` claim, so no host sees the
|
|
281
|
+
fork answer for, or retire, the package it was copied from.
|
|
282
|
+
- Bundled seeding never prunes other revisions of a plugin, so an operator-edited or uploaded
|
|
283
|
+
same-name copy survives every boot. The bundled-digests ledger selects the build's revision; the
|
|
284
|
+
other copies stay installed and unselected. Explicit install, update and replace still prune.
|
|
285
|
+
|
|
265
286
|
### Persistent plugin state (Layout B)
|
|
266
287
|
|
|
267
288
|
`@jini-ai/agent-plugins/persistent-state` is a build-free Node entry with declarations.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "charts-echarts",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Teaches the assistant to draw bar, stacked bar, line, area, pie, donut and scatter charts in chat with the Apache ECharts chart provider: finding the chart components with search_components, the data shape each one takes, several series in one chart through the generic chart.spec entry, axis names and number formats, and what falls back to a table. Know-how only: no code, no MCP server; the ECharts provider plugin (charts-echarts) draws the charts and switches this plugin on and off.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"extensions": {
|
|
8
|
+
"tovu": {
|
|
9
|
+
"id": "jini/charts-echarts-agent",
|
|
10
|
+
"displayName": "ECharts",
|
|
11
|
+
"summary": "Lets the assistant draw bar, stacked bar, line, area, pie, donut and scatter charts in chat, with several series. Ask for a chart of your data.",
|
|
12
|
+
"providerPlugin": "charts-echarts",
|
|
13
|
+
"provides": ["chart"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"chart",
|
|
18
|
+
"charts",
|
|
19
|
+
"graph",
|
|
20
|
+
"plot",
|
|
21
|
+
"bar-chart",
|
|
22
|
+
"stacked-bar-chart",
|
|
23
|
+
"line-chart",
|
|
24
|
+
"area-chart",
|
|
25
|
+
"pie-chart",
|
|
26
|
+
"donut-chart",
|
|
27
|
+
"scatter-plot",
|
|
28
|
+
"data-visualization",
|
|
29
|
+
"visualization",
|
|
30
|
+
"trend",
|
|
31
|
+
"breakdown",
|
|
32
|
+
"comparison",
|
|
33
|
+
"correlation",
|
|
34
|
+
"multi-series",
|
|
35
|
+
"chart-spec",
|
|
36
|
+
"echarts",
|
|
37
|
+
"charts-echarts"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: charts-echarts
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
last_updated: 2026-11-02
|
|
5
|
+
description: Use when the user asks for a chart, graph or plot of data (comparisons, trends, shares of a whole, stacked totals, correlations) and the ECharts chart provider is enabled. Covers finding the chart components, the data each takes, several series in one chart, axis names and number formats, and what falls back to a table.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Skill: Charts with ECharts
|
|
9
|
+
|
|
10
|
+
The ECharts provider draws charts the host shows in chat or on a canvas. You never write chart
|
|
11
|
+
code. You pick a chart component from the host's component catalog and hand it data as props.
|
|
12
|
+
|
|
13
|
+
## Find the components first
|
|
14
|
+
|
|
15
|
+
1. Call `search_components` with `"chart"` (or the shape you need, such as "stacked bar of revenue
|
|
16
|
+
and cost by month" or "scatter of height against weight").
|
|
17
|
+
2. Call `describe_component` on the one or two ids that fit, to read the exact props schema.
|
|
18
|
+
3. Render through the host's UI-rendering tool (the one that takes a component tree). Put the chart
|
|
19
|
+
inside a layout component if the tool needs a root.
|
|
20
|
+
|
|
21
|
+
The catalog is the source of truth. Ids and schemas below are what it lists today; if the catalog
|
|
22
|
+
differs, follow the catalog.
|
|
23
|
+
|
|
24
|
+
## Which kind
|
|
25
|
+
|
|
26
|
+
| Question the chart answers | Kind |
|
|
27
|
+
|---|---|
|
|
28
|
+
| How do categories compare (posts by status, sales by region)? | `bar` |
|
|
29
|
+
| How do parts add up per category (revenue split by product, per month)? | `stacked-bar` |
|
|
30
|
+
| How did a value change over time (sign-ups per month)? | `line` |
|
|
31
|
+
| How did a volume change over time, filled to the axis? | `area` |
|
|
32
|
+
| What share of a whole does each part take (pages vs posts)? | `pie` or `donut` |
|
|
33
|
+
| Do two measures move together (price against rating)? | `scatter` |
|
|
34
|
+
|
|
35
|
+
ECharts draws `bar`, `line`, `area`, `pie`, `donut`, `scatter` and `stacked-bar` only.
|
|
36
|
+
|
|
37
|
+
## The components and their data
|
|
38
|
+
|
|
39
|
+
**One series, simplest props** (`bar`, `line` and `pie` keep the shared single-series shapes):
|
|
40
|
+
|
|
41
|
+
- `plugin.echarts.bar-chart`, `plugin.echarts.line-chart`:
|
|
42
|
+
`{ data: [rows], categoryKey, valueKey, valueLabel?, color? }`.
|
|
43
|
+
- `plugin.echarts.pie-chart`: `{ data: [{ name, value }], color? }`. One row per slice.
|
|
44
|
+
|
|
45
|
+
**Every other kind, and any chart with several series: `{ spec }`**
|
|
46
|
+
|
|
47
|
+
`plugin.echarts.area-chart`, `.donut-chart`, `.scatter-chart` and `.stacked-bar-chart` take
|
|
48
|
+
`{ spec }` with that `spec.kind`. `chart.spec` takes the same `{ spec }` for any kind:
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{ "spec": {
|
|
52
|
+
"kind": "stacked-bar",
|
|
53
|
+
"categoryKey": "month",
|
|
54
|
+
"series": [{ "key": "revenue", "label": "Revenue" }, { "key": "cost", "label": "Cost" }],
|
|
55
|
+
"data": [{ "month": "Jan", "revenue": 120, "cost": 80 }, { "month": "Feb", "revenue": 150, "cost": null }],
|
|
56
|
+
"title": "Revenue and cost by month",
|
|
57
|
+
"axes": { "y": { "label": "USD", "format": { "notation": "compact" } } },
|
|
58
|
+
"provider": "plugin.echarts"
|
|
59
|
+
} }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- Every row carries the category and a number (or `null` for a gap) for every series key.
|
|
63
|
+
- Series keys are unique. There is at least one row and one series.
|
|
64
|
+
- `spec.provider: "plugin.echarts"` asks for ECharts when several chart providers are enabled.
|
|
65
|
+
- `axes.x.label` / `axes.y.label` name the axes. `format` (or `axes.x.format` / `axes.y.format`,
|
|
66
|
+
which win) takes `{ locale?, notation? }`, with `notation` `"standard"` or `"compact"` (1.5K).
|
|
67
|
+
|
|
68
|
+
## Kind rules
|
|
69
|
+
|
|
70
|
+
- `pie` and `donut`: exactly **one** series of numbers that are zero or more, with a total above
|
|
71
|
+
zero. `categoryKey` names the slices. No gaps.
|
|
72
|
+
- `scatter`: `categoryKey` is the numeric **x** of every point; each series is a **y**. Points with
|
|
73
|
+
a `null` y are left out.
|
|
74
|
+
- `bar`, `stacked-bar`, `line`, `area`: any number of series; `null` leaves a gap.
|
|
75
|
+
|
|
76
|
+
## What falls back
|
|
77
|
+
|
|
78
|
+
ECharts draws a spec only when the kind is one of its seven, there is a `categoryKey`, at least
|
|
79
|
+
one row and series, every value is a finite number or `null` (at least one is a number), the kind
|
|
80
|
+
rules above hold and any `locale` is a valid language tag. Anything else goes to another enabled
|
|
81
|
+
provider that accepts it, or shows as a table. Nothing is lost, but tell the user when you know
|
|
82
|
+
the result will be a table.
|
|
83
|
+
|
|
84
|
+
## Making it read well
|
|
85
|
+
|
|
86
|
+
- Aggregate first. Send the counts or totals to plot, not raw records.
|
|
87
|
+
- Bars: about 12 categories or fewer. Sort by value unless the order means something (months,
|
|
88
|
+
stages). In narrow spaces some category labels are skipped; the tooltip keeps every value.
|
|
89
|
+
- Stacked bars: 2 to 5 series, in a fixed order the user would read top to bottom.
|
|
90
|
+
- Lines and areas: time in order, oldest first; up to about 5 series.
|
|
91
|
+
- Pies and donuts: 6 slices or fewer, values that add up to a meaningful whole. Merge the small
|
|
92
|
+
ones into "Other". Use a bar chart when the user needs to compare similar values.
|
|
93
|
+
- Whole-number data (counts) gets whole-number axis ticks automatically.
|
|
94
|
+
- Leave `color` out unless the user asks. The host theme colours the chart.
|
|
95
|
+
- Give a short title that states what is shown, and a description when the chart makes a point
|
|
96
|
+
the title doesn't. Keep labels short and human: "Published", not `post_status_published`.
|
|
97
|
+
|
|
98
|
+
## Never
|
|
99
|
+
|
|
100
|
+
- Never paste ECharts, SVG or HTML chart code into the chat, a page or a file to draw a chart.
|
|
101
|
+
The provider owns the drawing; your job is the data and the component choice.
|
|
102
|
+
- Never invent data. If you can't get the numbers, say so and ask.
|
|
103
|
+
- Never claim a chart rendered when the host reported a refusal. Read the error, fix the props
|
|
104
|
+
(usually a missing key or a string where a number belongs), and try once more.
|
|
105
|
+
|
|
106
|
+
## When charts are off
|
|
107
|
+
|
|
108
|
+
If the chart entries `search_components` returns all report provider `core.table`, no chart
|
|
109
|
+
provider is enabled. The data will show as a table; say so, and tell the user an administrator can
|
|
110
|
+
turn the ECharts chart provider on.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
|
+
"name": "charts-recharts",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Teaches the assistant to draw bar, line and pie charts in chat with the Recharts chart provider: finding the chart components with search_components, the data shape each one takes, what Recharts draws and what falls back to a table, and the generic chart.spec entry for several series. Know-how only: no code, no MCP server; the Recharts provider plugin (charts-recharts) draws the charts and switches this plugin on and off.",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"extensions": {
|
|
8
|
+
"tovu": {
|
|
9
|
+
"id": "jini/charts-recharts-agent",
|
|
10
|
+
"displayName": "Recharts",
|
|
11
|
+
"summary": "Lets the assistant draw bar, line and pie charts in chat. Ask for a chart of your data.",
|
|
12
|
+
"providerPlugin": "charts-recharts",
|
|
13
|
+
"provides": ["chart"]
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"chart",
|
|
18
|
+
"charts",
|
|
19
|
+
"graph",
|
|
20
|
+
"plot",
|
|
21
|
+
"bar-chart",
|
|
22
|
+
"line-chart",
|
|
23
|
+
"pie-chart",
|
|
24
|
+
"data-visualization",
|
|
25
|
+
"visualization",
|
|
26
|
+
"trend",
|
|
27
|
+
"breakdown",
|
|
28
|
+
"chart-spec",
|
|
29
|
+
"recharts",
|
|
30
|
+
"charts-recharts"
|
|
31
|
+
]
|
|
32
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: charts-recharts
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
last_updated: 2026-11-02
|
|
5
|
+
description: Use when the user asks for a chart, graph or plot of data (counts, totals, trends over time, shares of a whole) and the Recharts chart provider is enabled. Covers finding the chart components, the data each takes, what Recharts draws, and when to use the generic chart.spec entry.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Skill: Charts with Recharts
|
|
9
|
+
|
|
10
|
+
The Recharts provider draws charts the host shows in chat or on a canvas. You never write chart
|
|
11
|
+
code. You pick a chart component from the host's component catalog and hand it data as props.
|
|
12
|
+
|
|
13
|
+
## Find the components first
|
|
14
|
+
|
|
15
|
+
1. Call `search_components` with `"chart"` (or the shape you need, such as "bar chart of counts by
|
|
16
|
+
category" or "trend over months").
|
|
17
|
+
2. Call `describe_component` on the one or two ids that fit, to read the exact props schema.
|
|
18
|
+
3. Render through the host's UI-rendering tool (the one that takes a component tree). Put the chart
|
|
19
|
+
inside a layout component if the tool needs a root.
|
|
20
|
+
|
|
21
|
+
The catalog is the source of truth. Ids and schemas below are what it lists today; if the catalog
|
|
22
|
+
differs, follow the catalog.
|
|
23
|
+
|
|
24
|
+
## Which kind
|
|
25
|
+
|
|
26
|
+
| Question the chart answers | Kind |
|
|
27
|
+
|---|---|
|
|
28
|
+
| How do categories compare (posts by status, sales by region)? | `bar` |
|
|
29
|
+
| How did a value change over time (sign-ups per month)? | `line` |
|
|
30
|
+
| What share of a whole does each part take (pages vs posts)? | `pie` |
|
|
31
|
+
|
|
32
|
+
Recharts draws `bar`, `line` and `pie` only. For any other kind (area, scatter, stacked bars), check
|
|
33
|
+
`search_components "chart"` for another provider; if none draws it, the data shows as a table.
|
|
34
|
+
|
|
35
|
+
## The components and their data
|
|
36
|
+
|
|
37
|
+
**One series, simplest props** (bar and line share one schema):
|
|
38
|
+
|
|
39
|
+
- `recharts.bar-chart`, `recharts.line-chart`:
|
|
40
|
+
`{ data: [rows], categoryKey, valueKey, valueLabel?, color? }`.
|
|
41
|
+
Every row has the category (`categoryKey`) and a number (`valueKey`).
|
|
42
|
+
`valueLabel` is the human name shown in the legend and tooltip ("Sign-ups"). Set it whenever
|
|
43
|
+
the key is not already readable.
|
|
44
|
+
- `recharts.pie-chart`: `{ data: [{ name, value }], color? }`. One row per slice.
|
|
45
|
+
|
|
46
|
+
The catalog may also list `plugin.recharts.bar-chart` and similar ids. They take the same props and
|
|
47
|
+
always ask Recharts by name.
|
|
48
|
+
|
|
49
|
+
**Any kind, one or more series: `chart.spec`**
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{ "spec": {
|
|
53
|
+
"kind": "bar",
|
|
54
|
+
"categoryKey": "month",
|
|
55
|
+
"series": [{ "key": "signups", "label": "Sign-ups" }],
|
|
56
|
+
"data": [{ "month": "Jan", "signups": 12 }, { "month": "Feb", "signups": 19 }],
|
|
57
|
+
"title": "Sign-ups by month"
|
|
58
|
+
} }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
- Every row carries the category and a number (or `null` for a gap) for every series key.
|
|
62
|
+
- Series keys are unique. There is at least one row and one series.
|
|
63
|
+
- For a pie, `categoryKey` names the slices and the single series holds the values.
|
|
64
|
+
- `spec.provider: "plugin.recharts"` asks for Recharts when several providers are enabled.
|
|
65
|
+
- `chart.spec` is always in the catalog. With no provider enabled, it shows the data as a table.
|
|
66
|
+
|
|
67
|
+
## What Recharts draws, and what falls back
|
|
68
|
+
|
|
69
|
+
Recharts draws a spec only when all of these hold:
|
|
70
|
+
|
|
71
|
+
- the kind is `bar`, `line` or `pie`;
|
|
72
|
+
- there is exactly **one** series and a `categoryKey`;
|
|
73
|
+
- every value is a finite number (no `null` gaps);
|
|
74
|
+
- the spec sets no `axes` or `format`.
|
|
75
|
+
|
|
76
|
+
Anything else goes to another enabled provider that accepts it, or shows as a table. Nothing is
|
|
77
|
+
lost, but tell the user when you know the result will be a table (for example two series with only
|
|
78
|
+
Recharts enabled). Offer one chart per series instead.
|
|
79
|
+
|
|
80
|
+
## Making it read well
|
|
81
|
+
|
|
82
|
+
- Aggregate first. Send the counts or totals to plot, not raw records.
|
|
83
|
+
- Bars: about 12 categories or fewer. Sort by value unless the order means something (months,
|
|
84
|
+
stages). Long labels wrap onto up to three lines; in narrow spaces some labels are skipped, and
|
|
85
|
+
the full label stays in the tooltip.
|
|
86
|
+
- Lines: time in order, oldest first. Long series are fine; the axis thins its own labels.
|
|
87
|
+
- Pies: 6 slices or fewer, values that add up to a meaningful whole. Merge the small ones into
|
|
88
|
+
"Other". Use a bar chart when the user needs to compare similar values.
|
|
89
|
+
- Whole-number data (counts) gets whole-number axis ticks automatically. Do not round other data
|
|
90
|
+
to force it.
|
|
91
|
+
- Leave `color` out unless the user asks. The host theme colours the chart.
|
|
92
|
+
- Give a short title that states what is shown ("Posts by status"), and a description when the
|
|
93
|
+
chart makes a point the title doesn't.
|
|
94
|
+
- Keep labels short and human: "Published", not `post_status_published`.
|
|
95
|
+
|
|
96
|
+
## Never
|
|
97
|
+
|
|
98
|
+
- Never paste Recharts, React, SVG or HTML chart code into the chat, a page or a file to draw a
|
|
99
|
+
chart. The provider owns the drawing; your job is the data and the component choice.
|
|
100
|
+
- Never invent data. If you can't get the numbers, say so and ask.
|
|
101
|
+
- Never claim a chart rendered when the host reported a refusal. Read the error, fix the props
|
|
102
|
+
(usually a missing key or a string where a number belongs), and try once more.
|
|
103
|
+
|
|
104
|
+
## When charts are off
|
|
105
|
+
|
|
106
|
+
If the chart entries `search_components` returns all report provider `core.table`, no chart
|
|
107
|
+
provider is enabled. The data will show as a table; say so, and tell the user an administrator can
|
|
108
|
+
turn the Recharts chart provider on.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Cloudflare — implementation notes
|
|
2
|
+
|
|
3
|
+
Host-specific notes moved out of the host's core comments (2026-10-22, vendor-to-plugins). The host describes these
|
|
4
|
+
behaviours generically; the vendor detail lives here.
|
|
5
|
+
|
|
6
|
+
- **Credentials.** `accountId` is HARD required for Pages publishes; a credential missing it fails with a
|
|
7
|
+
`DeployError` naming the field. The optional Zone ID may be submitted blank, meaning "not provided".
|
|
8
|
+
- **Stored `projectName`.** Accepted and stored although, when written, the Pages target derived its project
|
|
9
|
+
name from the publish call's label, not from stored config.
|
|
10
|
+
- **Account label.** No reviewed account field is read from the verify response; the account-label heal
|
|
11
|
+
skips these credentials.
|
|
12
|
+
- **Security headers.** Root `_headers` is applied only when sent as its own upload field, never as an asset
|
|
13
|
+
(see `lib/pages-upload.mjs`, `skills/cloudflare/references/pages.md`).
|
|
14
|
+
- **Forms receiver hides the sent form.** On 2026-10-19 a theme's `.oh-form form{display:grid}` kept a sent
|
|
15
|
+
form on screen after the Cloudflare Worker's script set `hidden` on it; the host's form CSS now forces
|
|
16
|
+
`[hidden]{display:none!important}`.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { acknowledgeFormSubmissions, pullFormSubmissions } from '../lib/forms.mjs';
|
|
2
|
+
|
|
3
|
+
/** Form destination `cloudflare-d1`, co-hosted on Cloudflare Workers: publishing to Workers installs
|
|
4
|
+
* the receiver and its D1 database in the site's own Worker (`lib/target.mjs`); this module only
|
|
5
|
+
* reads and acknowledges what that receiver stored. */
|
|
6
|
+
export default {
|
|
7
|
+
pull: pullFormSubmissions,
|
|
8
|
+
ack: acknowledgeFormSubmissions,
|
|
9
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Cloudflare-only HTTP owner. All network effects use the injected, traced host transport. */
|
|
2
|
+
export const API_ROOT = 'https://api.cloudflare.com/client/v4';
|
|
3
|
+
export const segment = ({ value }, _options = {}) => encodeURIComponent(value);
|
|
4
|
+
/** Cloudflare rejects `per_page` above 10 on the Pages projects list (error 8000024, HTTP 400). */
|
|
5
|
+
export const PAGES_PROJECTS_PAGING = { perPage: 10, maxPages: 11 };
|
|
6
|
+
/** Cloudflare caps `per_page` at 50 on the zones list (the default 100 is refused). */
|
|
7
|
+
export const ZONES_PAGING = { perPage: 50, maxPages: 20 };
|
|
8
|
+
|
|
9
|
+
/** Safe, stable refusal; upstream bodies can contain credentials and never cross this boundary. */
|
|
10
|
+
export function refusal({ kit, code, message, status = 400 }, _options = {}) {
|
|
11
|
+
const error = new kit.DeployError(`${code}: ${message}`, status, { errorCode: code });
|
|
12
|
+
error.code = code;
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function nextStep({ status }, _options = {}) {
|
|
17
|
+
if (status === 401 || status === 403) return 'Check the token permissions and account/zone scope in the Cloudflare skill, then preview again.';
|
|
18
|
+
if (status === 429) return 'Cloudflare is rate limiting this account. Wait, then preview again.';
|
|
19
|
+
return 'Check the named resource in Cloudflare, then preview again.';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Bind credential and HTTP once. Requests never follow redirects with a bearer token. */
|
|
23
|
+
export function createApi({ credential, kit }, _options = {}) {
|
|
24
|
+
if (!credential.token || !credential.accountId) throw refusal({ kit, code: 'CLOUDFLARE_CREDENTIAL_REQUIRED', message: 'Save a Cloudflare API token and Account ID in the secure connection form.' });
|
|
25
|
+
const accountPath = `/accounts/${segment({ value: credential.accountId })}`;
|
|
26
|
+
async function response({ path, method = 'GET', body, jwt }, options = {}) {
|
|
27
|
+
const init = requestInit({ method, body, token: jwt ?? credential.token });
|
|
28
|
+
let resp;
|
|
29
|
+
try { resp = await kit.fetch(`${API_ROOT}${path}`, init, { timeoutMs: options.timeoutMs ?? kit.timeouts.QUICK }); }
|
|
30
|
+
catch { throw refusal({ kit, code: 'CLOUDFLARE_UNREACHABLE', message: `${method} ${path} could not reach Cloudflare. Check your connection and retry.`, status: 502 }); }
|
|
31
|
+
if (options.allowMissing && resp.status === 404) return { missing: true };
|
|
32
|
+
return decodeResponse({ resp, method, path, kit });
|
|
33
|
+
}
|
|
34
|
+
async function request(input, options = {}) {
|
|
35
|
+
const json = await response(input, options);
|
|
36
|
+
if (json.missing) return null;
|
|
37
|
+
return json.result ?? json;
|
|
38
|
+
}
|
|
39
|
+
async function list({ path }, { maxPages = 100, perPage = 100 } = {}) {
|
|
40
|
+
const items = [];
|
|
41
|
+
let pagination = { count: 0, pages: 0 };
|
|
42
|
+
for (let page = 1; page <= maxPages; page += 1) {
|
|
43
|
+
const separator = path.includes('?') ? '&' : '?';
|
|
44
|
+
const json = await response({ path: `${path}${separator}page=${page}&per_page=${perPage}` });
|
|
45
|
+
if (!Array.isArray(json.result)) throw refusal({ kit, code: 'CLOUDFLARE_INVENTORY_UNKNOWN', message: `${path} did not return a complete list. Check read permissions and preview again.`, status: 502 });
|
|
46
|
+
items.push(...json.result);
|
|
47
|
+
pagination = paginationState({ previous: pagination, info: json.result_info });
|
|
48
|
+
if (items.length > maxPages * perPage) throw refusal({ kit, code: 'CLOUDFLARE_INVENTORY_UNKNOWN', message: `${path} exceeded the inventory item limit. Narrow the account and preview again.`, status: 409 });
|
|
49
|
+
const more = hasNextPage({ info: json.result_info, page, perPage, count: json.result.length });
|
|
50
|
+
assertCompletePage({ kit, path, pagination, itemCount: items.length, lastCount: json.result.length, more });
|
|
51
|
+
if (!more) return items;
|
|
52
|
+
}
|
|
53
|
+
throw refusal({ kit, code: 'CLOUDFLARE_INVENTORY_UNKNOWN', message: `${path} exceeded the ${maxPages}-page inventory limit. Narrow the account scope or contact support; no domain changes are safe.`, status: 409 });
|
|
54
|
+
}
|
|
55
|
+
return { request, list, accountPath, accountId: credential.accountId, zoneId: credential.zoneId, kit };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function paginationState({ previous, info }, _options = {}) {
|
|
59
|
+
return { count: Math.max(previous.count, Number(info?.total_count) || 0), pages: Math.max(previous.pages, Number(info?.total_pages) || 0) };
|
|
60
|
+
}
|
|
61
|
+
function assertCompletePage({ kit, path, pagination, itemCount, lastCount, more }, _options = {}) {
|
|
62
|
+
if ((!more && itemCount < pagination.count) || (lastCount === 0 && pagination.pages > 1)) throw refusal({ kit, code: 'CLOUDFLARE_INVENTORY_UNKNOWN', message: `${path} returned an incomplete inventory page. Refresh read permissions and preview again; no domain changes are safe.`, status: 409 });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function decodeResponse({ resp, method, path, kit }, _options = {}) {
|
|
66
|
+
let json;
|
|
67
|
+
try { json = await resp.json(); }
|
|
68
|
+
catch { throw refusal({ kit, code: 'CLOUDFLARE_INVALID_RESPONSE', message: `${method} ${path} returned unreadable data. Retry; if it persists check Cloudflare status.`, status: 502 }); }
|
|
69
|
+
if (json === null || typeof json !== 'object') throw refusal({ kit, code: 'CLOUDFLARE_INVALID_RESPONSE', message: `${method} ${path} returned no usable response object. Retry the request and check Cloudflare status.`, status: 502 });
|
|
70
|
+
if (!resp.ok || json?.success === false) throw apiFailure({ kit, json, method, path, status: resp.status });
|
|
71
|
+
return json;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function apiFailure({ kit, json, method, path, status }, _options = {}) {
|
|
75
|
+
const error = refusal({ kit, code: 'CLOUDFLARE_API_ERROR', message: `${method} ${path} failed (HTTP ${status}). ${nextStep({ status })}`, status });
|
|
76
|
+
// Classify duplicate creation races without returning Cloudflare's potentially secret-bearing text.
|
|
77
|
+
const errors = Array.isArray(json.errors) ? json.errors : [];
|
|
78
|
+
error.details.alreadyExists = errors.some(item => /already exist|already bound|already been taken|already in use|duplicate/i.test(String(item?.message)));
|
|
79
|
+
return error;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function requestInit({ method, body, token }, _options = {}) {
|
|
83
|
+
const headers = { Authorization: `Bearer ${token}` };
|
|
84
|
+
const init = { method, headers, redirect: 'error' };
|
|
85
|
+
if (body === undefined) return init;
|
|
86
|
+
if (body instanceof FormData) init.body = body;
|
|
87
|
+
else { headers['Content-Type'] = 'application/json'; init.body = JSON.stringify(body); }
|
|
88
|
+
return init;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Empty pages before a reported end are incomplete, not evidence of absence. */
|
|
92
|
+
function hasNextPage({ info, page, perPage, count }, _options = {}) {
|
|
93
|
+
if (Number.isFinite(info?.total_pages)) return page < info.total_pages;
|
|
94
|
+
if (Number.isFinite(info?.total_count)) return page * (info.per_page ?? perPage) < info.total_count;
|
|
95
|
+
return count >= perPage;
|
|
96
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { refusal } from './api.mjs';
|
|
3
|
+
|
|
4
|
+
/** Stable Pages/Workers dedup key; native crypto avoids a native/WASM dependency.
|
|
5
|
+
* Retains Pages' historical data+extension hash so existing uploads stay deduplicated. */
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic content hash used purely as Cloudflare Pages' per-asset
|
|
8
|
+
* dedup key (the direct-upload protocol needs a stable 32-hex-char key per
|
|
9
|
+
* file, not a specific algorithm). Swapped from the OD origin's
|
|
10
|
+
* `blake3-wasm` to Node's built-in `crypto` so this package carries no
|
|
11
|
+
* native/WASM dependency — see `source-map.md`.
|
|
12
|
+
*/
|
|
13
|
+
export function assetHash({ file, data }, _options = {}) {
|
|
14
|
+
const extension = file.includes('.') ? file.slice(file.lastIndexOf('.') + 1) : '';
|
|
15
|
+
return createHash('sha256').update(`${Buffer.from(data).toString('base64')}${extension}`).digest('hex').slice(0, 32);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Root _headers/_redirects are configuration, never public assets. A nested copy remains an asset.
|
|
19
|
+
* Host security headers replace the site's root _headers because they are host policy.
|
|
20
|
+
* @complexity O(n + bytes) time and space; n is capped by the selected hosting plan. */
|
|
21
|
+
export function prepareAssets({ files, responseHeaders, kit }, { maxFiles = 20_000 } = {}) {
|
|
22
|
+
const normalized = files.map(file => ({ ...file, file: file.file.replaceAll('\\', '/').replace(/^\.\//, '') }));
|
|
23
|
+
const headers = responseHeaders && Object.keys(responseHeaders).length > 0;
|
|
24
|
+
const supplied = headers ? [...normalized.filter(file => file.file !== '_headers'), { file: '_headers', data: renderHeaders({ headers: responseHeaders }) }] : normalized;
|
|
25
|
+
if (supplied.length > maxFiles) throw refusal({ kit, code: 'CLOUDFLARE_ASSET_COUNT_LIMIT', message: `${supplied.length} files exceed this destination's ${maxFiles}-file limit (including generated files). Reduce the export or explicitly select a supported paid Workers plan.` });
|
|
26
|
+
const manifest = {};
|
|
27
|
+
const byHash = new Map();
|
|
28
|
+
const config = {};
|
|
29
|
+
for (const file of supplied) addAsset({ file, manifest, byHash, config, kit });
|
|
30
|
+
return { manifest, byHash, config };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function addAsset({ file, manifest, byHash, config, kit }, _options = {}) {
|
|
34
|
+
if (!file.file || file.file.startsWith('/') || file.file.split('/').includes('..')) throw refusal({ kit, code: 'CLOUDFLARE_ASSET_PATH', message: `Export path ${file.file} must be relative without '..'. Fix the export and retry.` });
|
|
35
|
+
const data = Buffer.from(file.data);
|
|
36
|
+
if (data.length > 25 * 1024 * 1024) throw refusal({ kit, code: 'CLOUDFLARE_ASSET_SIZE_LIMIT', message: `${file.file} exceeds 25 MiB. Resize the file and export again.` });
|
|
37
|
+
if (file.file === '_headers' || file.file === '_redirects') { config[file.file] = data.toString('utf8'); return; }
|
|
38
|
+
const path = `/${file.file}`;
|
|
39
|
+
if (Object.hasOwn(manifest, path)) throw refusal({ kit, code: 'CLOUDFLARE_ASSET_PATH', message: `Duplicate export path ${path}. Fix the export and retry.` });
|
|
40
|
+
const hash = assetHash({ file: file.file, data });
|
|
41
|
+
manifest[path] = { hash, size: data.length };
|
|
42
|
+
byHash.set(hash, { hash, data, contentType: file.contentType ?? 'application/octet-stream' });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function renderHeaders({ headers }, _options = {}) {
|
|
46
|
+
const lines = Object.entries(headers).map(([name, value]) => ` ${name}: ${value}`).join('\n');
|
|
47
|
+
return `/*\n${lines}\n`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Respect both Pages limits. A single oversize request is refused before any upload.
|
|
51
|
+
* @complexity O(n) over unique missing assets; at most 100 per request. */
|
|
52
|
+
export function chunkUploads({ files }, { maxFiles = 100, maxBytes = 75 * 1024 * 1024 } = {}) {
|
|
53
|
+
const chunks = [];
|
|
54
|
+
let chunk = [];
|
|
55
|
+
let bytes = 2; // JSON array brackets.
|
|
56
|
+
for (const file of files) {
|
|
57
|
+
const size = Math.ceil(file.data.length / 3) * 4 + Buffer.byteLength(file.contentType) + file.hash.length + 128;
|
|
58
|
+
if (size + 2 > maxBytes) throw new Error('CLOUDFLARE_UPLOAD_LIMIT: One asset cannot fit in the upload request. Reduce its size.');
|
|
59
|
+
if (chunk.length >= maxFiles || bytes + size > maxBytes) { chunks.push(chunk); chunk = []; bytes = 2; }
|
|
60
|
+
chunk.push(file);
|
|
61
|
+
bytes += size;
|
|
62
|
+
}
|
|
63
|
+
if (chunk.length) chunks.push(chunk);
|
|
64
|
+
return chunks;
|
|
65
|
+
}
|