Commit Graph
3 Commits
Author SHA1 Message Date
pastilhasandClaude Opus 5 0a1f1ce6df publish desktop, and stop counting node_modules as a plugin
The catalogue row and the bundled offline fallback, so the plugin installs from the marketplace like
every other. Listed honestly: its permission is owner-only at every path, and its websocket is not
mounted yet — the README and BLOCKERS.md say so rather than letting an install discover it.

marketplace-sync globs plugins/ itself rather than going through discoverPlugins, so the node_modules
skip added there did not cover it. Same fix, second place: it was reporting 15/16 and 16/17.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 00:45:05 +00:00
pastilhasandClaude Opus 5 5b486ed019 build marketplace icon URLs from HEAD, not from a branch name
Every available plugin drew a broken image on any server reading the remote
catalogue. The URLs said /raw/branch/main/, and four plugin repositories were
created on `master` — so those 404'd, and the others only worked by accident of
which branch somebody happened to use.

The comment above the builder already said "a repo whose default branch is
`master` gets a 404", marked [open]. It then happened. A warning in a comment
is not a guard.

Gitea resolves /raw/HEAD/ to whatever the default branch is, so the question
stops being asked. Verified against both spellings, and all 12 icons in the
live catalogue now answer 200.

It did not show on alpha because every plugin there is INSTALLED, and an
installed plugin's tile is served locally from /plugins/<name>/ rather than
from the catalogue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 10:55:10 +00:00
pastilhasandClaude Opus 5 921fbff9fe marketplace.officer.dev: the catalogue becomes a service
The plugins page read a TypeScript constant. It now reads a public API backed by
its own database, and falls back to that constant when it cannot.

── Four pieces ──

`src/databases/marketplace_db` — a second Postgres database, `officer_marketplace`.
Two tables, split by owner and lifetime: `plugins` is the registry (app name,
developer, clone URL) and is authoritative; `plugin_metadata` is a CACHE of what
each repository says about itself, so the website can render without cloning three
repos per page load. Keeping them apart is what makes the cache safe to be wrong —
a refresh can never lose the fact that a plugin exists.

`scripts/marketplace-sync.ts` — fills it from the plugin repositories. Reads each
`manifest.ts` (imported, since a manifest is TypeScript), derives `has` from the
directory rather than trusting a declaration, and rewrites SSH remotes to HTTPS:
`example` and `offscale` had `git@gitea…` origins, which would have handed every
installer a URL that only works on the machine that published it.

`src/servers/marketplace/server.tsx` — its own process, its own port, two GET
routes and no others. Not part of the platform server: it has no users, no
sessions and no request it answers differently per caller, and mounting a public
unauthenticated surface inside the process holding mail, a wallet and passkeys
means writing permission-gate exemptions that later turn out to be too wide.

`src/apps/marketplace` — the public site. A window, deliberately: nothing here
installs anything, because installing means cloning a repo and starting a process
on someone else's machine. Every plugin page ends at "install it from your own
Officer".

── The platform side is the part that had to be careful ──

`fetchCatalogue()` now answers live → cached → bundled, in that order, and never
throws. The plugins page is where an owner manages plugins they ALREADY have, so
the store being down must not take that page with it. Network responses are
validated per entry — not against a hostile marketplace, which has already won if
the owner installs what it lists, but against a wrong one: a proxy serving HTML,
a half-deployed API, a field renamed on one side. A non-https `source` is refused
outright because that string reaches `git clone`.

8 tests on the degradation path. Verified end to end against the running service:
live catalogue, cached answer surviving the store dying, bundled fallback.

── One thing found on the way ──

`bunfig.toml` and CLAUDE.md both document `bun install --no-frozen-lockfile` as
the way to change a dependency. Bun 1.3.14 has no such flag — only
`--frozen-lockfile` — so the documented escape hatch does not work. Sidestepped by
not making marketplace_db a workspace member (relative imports, zero lockfile
change), and `bun install` is frozen-clean again. Worth fixing the docs.

tsgo clean, 873 pass / 7 fail unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 04:07:49 +00:00