bf3bfab3784d493909f2fa52e794a0228eceefea
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
8b8d51548b | docs(plugins): add plugin authoring skill (#1828) | ||
|
|
52946b9159 |
docs: document the plugin marketplace (#1805)
* docs: document the plugin marketplace Add a public Plugin Marketplace page covering both audiences: operators using the in-app catalog (Browse/Installed tabs, one-click install, Update to vX, the Sources dialog for team/corporate registries, plugin icons, feature-flag gating, sideloading still works) and plugin authors publishing into it (one-repo-per-plugin GitHub Release assets, the optional manifest icon field, submitting a plugins.yaml PR to the official catalog, GitHub-stars-only ranking, and hosting your own index.json source). Also cross-link the new page from plugins.md (replacing the now-false "No plugin marketplace" security note), plugins-authoring.md, and plugins-manifest.md (new icon field row); register it in meta.json and coverage.json. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: correct plugin-registry CI validation claims The registry index-build workflow does not validate plugins.yaml against schema.json, nor does build-index.mjs compare the entry id to the manifest id — bad entries are skipped-and-logged, and CI only fails when a non-empty list resolves to zero built entries. Reword the submission steps (in both the marketplace docs page and plugin-registry/README.md) to describe what the workflow actually does: it resolves each entry against the GitHub API and requires a correctly named <id>-<version>.tar.gz release asset, with maintainer approval as the real gate. Also clarify that plugins.yaml `categories` are free-form curation tags, not the manifest's category enum. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci: split plugin-registry Pages deploy into its own job The single build-and-deploy job bound the `github-pages` environment at the job level, so GitHub evaluated that environment's protection rules on every PR run — failing the check even though the deploy steps were gated to non-PR events. This broke every PR touching plugin-registry/**, which is exactly what a plugin-submission PR does. Split into a PR-safe `build` job (no environment) and a non-PR-gated `deploy` job that owns the `github-pages` environment and runs only `deploy-pages`. Behavior on push/schedule/workflow_call is unchanged; PRs now run build+tests without tripping the environment gate. The star refresh calls this workflow by file path, so the job rename is safe. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: describe index-build asset resolution accurately build-index.mjs picks the <id>-<version>.tar.gz release asset when present but falls back to the release's first .tar.gz otherwise, and only errors when a release has no .tar.gz at all — it does not hard-fail a misnamed asset. Reword the submission steps to present the exact asset name as an authoring instruction (so the index resolves the right asset) rather than a builder-enforced gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Revert "ci: split plugin-registry Pages deploy into its own job" This reverts commit |
||
|
|
bc2f310e73 |
feat(plugins): add plugin marketplace with curated registry and updates (#1798)
* feat(plugins): add plugin marketplace with curated registry and updates Discover and install plugins from inside kandev instead of only by URL/upload. - Backend marketplace subpackage: SQLite-backed sources, catalog fetch/merge (dedup, install_state, degraded-source handling), and the /api/plugins/ marketplace[/sources] API. Install reuses the existing install-by-URL pipeline. - Frontend: Settings > Plugins gains a Browse tab (search/category/star sort), source management, one-click install, and an Update button on the Installed tab when the catalog has a newer version. Plugins may ship an icon (manifest `icon` -> catalog icon_url) with a letter-tile fallback. - Registry infra: plugin-registry/ (curated plugins.yaml + schema.json) and two GitHub Actions (zero-dependency Node build-index.mjs -> index.json on Pages, daily star refresh). Ships empty at launch; the empty catalog is valid. - Spec: docs/specs/plugins/marketplace.md. No download telemetry; stars only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(plugins): address marketplace PR review feedback - CodeQL: stub fetch routers keyed off a path segment, not a hostname substring. - Nullable stars end to end: a failed star lookup stays unknown (not 0) and sorts last, instead of decoding null -> 0 and corrupting ranking. - Fetch marketplace sources concurrently and dedupe identical in-flight downloads (singleflight), so a slow source can't serialize the Browse tab. - Register /marketplace routes before the /:id wildcard (gin tree ordering). - Duplicate source URL returns 409 ErrDuplicateSource, not a raw SQL error. - Browse: filter category client-side so the category dropdown never collapses. - marketplaceInstall swallows after toasting (no unhandled promise rejection). - Source badge keys off source_id, not a name substring. - build-index: read block-sequence manifest categories, not just inline. - Registry index workflow validates on pull_request; Pages deploy gated to push/schedule/dispatch. Spec: align package_sha256 (advisory in v1) + sort enum. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(plugins): guard catalog repo_url against javascript: href injection repo_url comes from an untrusted index.json, so a malicious/compromised source could set a javascript: URL that runs in the operator's session when the Repo link is clicked (rel/target don't block it). Only render the anchor for http(s) schemes. Adds regression tests for the scheme guard and null-star rendering. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(plugins): require https for remote sources; test oversized download - Reject remote plain-http marketplace sources (MITM catalog-poisoning risk); loopback http stays allowed for local dev/e2e fixtures. - Add the missing oversized-response assertion to TestDownloadRejectsNon200AndOversized so the maxIndexBytes memory bound has coverage. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(plugins): detach source download from singleflight leader's context Two concurrent Browse opens after a cache Refresh share one singleflight download; if the leader's request context is canceled (it navigates away), the waiters must not inherit that cancellation. Use context.WithoutCancel so values are preserved but the leader can't cancel everyone; the client's 20s timeout still bounds the download. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Kandev Agent <agent@kandev.dev> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |