the catalogue was the only way a remote plugin could arrive, so "build one and give it to a friend" had no route. the first real case is a plugin that must never be listed: fofos/fofos-admin is one website's admin panel, useful to exactly one person. MUCH smaller than the todo assumed. fetchPlugin already cloned, validated the URL, refused to overwrite an existing directory, checked for a manifest and removed a failed clone. exactly one line tied it to the marketplace — where the string came from. so this is a source parameter threaded through, and a route. THE NAME COMES FROM THE REPOSITORY, because appName is the DIRECTORY name and is never declared in a manifest — the id cannot disagree with where the code sits. that decision is inherited here rather than remade: the repo name becomes the directory, the route segment, the table prefix and the sidecar suffix. so it is checked against ^[a-z0-9]+(-[a-z0-9]+)*$ BEFORE anything is cloned; a half-clone under an unusable name reads as a broken plugin rather than an absent one. the URL is validated identically whether it came from the catalogue or from a person, because the catalogue is not a trust boundary — it is just where a string came from, and the same string reaches the same argv. what differs is who may ask: this route is owner-only, and the owner is choosing what code runs rather than choosing from a list somebody curated. validation is not review, and nothing here reads what it cloned. caught by testing: the source reached installPlugin and stopped there — the fetchPlugin call still had the old two arguments, so a URL install fell back to the catalogue and failed with "no catalogue entry". the guard tests are what surfaced it, because the error named the wrong cause. verified end to end against fofos-admin — cloned, schema applied, mounted, dock pinned — then purged, since officer-dev is not that website's machine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
29 lines
1.3 KiB
Markdown
29 lines
1.3 KiB
Markdown
---
|
|
id: plugin-ecosystem/no-install-from-git-url
|
|
title: "No install-from-git-URL"
|
|
theme: plugin-ecosystem
|
|
status: done
|
|
created: 2026-08-18
|
|
date_source: section
|
|
order: 3
|
|
---
|
|
|
|
**No install-from-git-URL.** The catalogue is the only route for a remote plugin, so the
|
|
"build your own, give it to a friend" path has no affordance. Small, and needed before the
|
|
unofficial channel is real.
|
|
|
|
**Done 2026-08-28.** `POST /api/plugins/install-from-url`, owner-only, taking `{ url }` and optionally
|
|
`{ appName }`.
|
|
|
|
Smaller than this note assumed: `fetchPlugin` already cloned, validated the URL, refused to overwrite an
|
|
existing directory, checked for a `manifest.ts` and cleaned up a failed clone. One line tied it to the
|
|
catalogue — where the URL came from. So the change is a source parameter threaded through, plus a route.
|
|
|
|
The name comes from the repository, because `appName` IS the directory name and is never declared in a
|
|
manifest. That is checked against `^[a-z0-9]+(-[a-z0-9]+)*$` BEFORE cloning: it becomes the route
|
|
segment, the table prefix and the pm2 process name, and a half-clone under an unusable name reads as a
|
|
broken plugin rather than an absent one.
|
|
|
|
The first real user is `fofos/fofos-admin` — one website's admin panel, which should never be in a
|
|
registry. Verified end to end against it and then purged, since this is not that website's machine.
|