mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
Fix/sdk screenshot gate parity (#2164)
* fix(sdk): grade docs/screenshot.png exactly, like the registry gate * docs(wiki): document plugin MCP tools and the exact screenshot gate * test(sdk): cover the preflight screenshot probe
This commit is contained in:
@@ -72,6 +72,7 @@ ungranted capability is physically unreachable**, not just disallowed. See
|
||||
| `hook:trip-card-provider` | Add small **badges** to the dashboard trip cards via the `hooks.tripCardProvider` provider hook | Implement `TripCardProvider` in `hooks` — `getCards(tripIds, ctx)` is called ONCE with all the trip cards currently on the user's dashboard (each already access-checked for the acting user), returns `{tripId, id, label, value?, icon?, tone?, url?}[]`. **Declarative only** — plugin JS never runs on the dashboard. The host String-coerces + length-caps every field, enum-whitelists the tone, allowlists the url (http/https/mailto), caps the count (≤4 badges per trip, ≤240 total per plugin) and drops any badge whose `tripId` the dashboard didn't ask about; a failing provider is skipped. Exposed at `GET /api/trip-card-contributions?tripIds=…`. |
|
||||
| `hook:notification-channel` | Register a new **notification channel** (Gotify, Pushover, …) via the `hooks.notificationChannel` provider hook | Implement `NotificationChannel` in `hooks` — `send(msg, config, ctx)` receives a notification TREK has **already rendered** into the recipient's language (`{event, title, body, url?, tripName?}`) plus that recipient's own decrypted `scope:'user'` settings as `config`. Unlike every other hook this one is **host-initiated for an arbitrary recipient**, so it runs **userless**: `ctx.settings.get()` returns `undefined` and trip reads are refused — the recipient's credentials arrive as `config` precisely so the plugin never gains the right to read anything *as* them. Optional `test(config, ctx)` backs the "Send test" button. Declare the channel with `capabilities.notificationChannel: { title?, events? }`; `title` names the column in the preferences matrix (default: the plugin's name) and `events` may **narrow** which events it carries (default: every non-admin event — admin-scoped events are never deliverable to a plugin). Throw on failure: the host logs it and isolates it, so a dead channel can't stop the others. |
|
||||
| `hook:user-data` | Honour GDPR **data-subject rights** — erase and export the data the plugin stores about a user — via the `deleteUserData` / `exportUserData` handlers | Implement `deleteUserData({userId}, ctx)` and/or `exportUserData({userId}, ctx)` on the plugin definition (not on `ctx`). Both are **userless** (no acting user; the plugin only learns the `userId` and touches its OWN db). When a TREK account is deleted, the host queues an erasure for every plugin holding this grant and retries it **durably** until the plugin ACKs — even across restarts — so implement `deleteUserData` idempotently. `exportUserData` returns a JSON-serialisable value the host aggregates for an admin at `GET /api/admin/plugins/user-data/:userId/export`. |
|
||||
| `mcp:tools` | Publish **MCP tools** on TREK's own MCP server via the `hooks.mcpToolProvider` provider hook | Declare the tools in `capabilities.mcpTools` (≤8; name, required description, optional JSON-Schema `inputSchema` + MCP annotations) and implement `mcpToolProvider` in `hooks` — `tools: string[]` plus one `callTool({name, args}, ctx)` for all of them. Only the **intersection** of the manifest declaration and the `tools` array is advertised (silently — keep the lists identical), each as `plugin_<id>_<name>` to assistants holding the opt-in `plugins:use` OAuth scope. `callTool` runs **as the requesting MCP user** (route-like; membership-checked reads, 15 s timeout) and this grant unlocks no `ctx` method of its own — what a tool can do is set by the plugin's other grants. Arguments are validated against the declared schema before the plugin runs; results are capped (64 KiB / 32 blocks) and every string sanitised; annotation hints are clamped against the plugin's grants (`readOnlyHint` can only be lowered, `openWorldHint` is forced true with any `http:outbound*`). Any lifecycle/grant change closes live MCP sessions so the surface is re-advertised. The only hook permission not named `hook:*`. |
|
||||
| `http:outbound` / `http:outbound:<host>` | Make outbound network requests | **Requires** a non-empty `egress[]` — unless the manifest sets `operatorEgress: true`. Only a **per-host** `http:outbound:<host>` actually opens a host at runtime — see below. |
|
||||
|
||||
## Outbound network — `http:outbound` vs `http:outbound:<host>`
|
||||
@@ -160,7 +161,7 @@ flow is in [[Publishing a Plugin|Plugin-Publishing]].
|
||||
|---|---|
|
||||
| `trek-plugin create [name]` | Scaffold a plugin. With no name it runs an interactive wizard (id, type, author, permissions); with a name it takes `--type`/`--author`/`--permissions` flags. |
|
||||
| `trek-plugin dev [dir]` | Run the plugin locally with a real request loop + hot reload — no full TREK. The injected `ctx` enforces your granted permissions, `db:own` is a real SQLite file, routes serve under `/api/<path>`, and page/widget UI at `/ui`. |
|
||||
| `trek-plugin validate [dir]` | Manifest + layout checks, and the gate for publishing: parses the manifest with the same rules as install, then runs every registry gate that can be answered from the working tree — exiting non-zero on any of them. It **fails** if `README.md` is missing, is missing one of the four required sections, has too little prose, still holds template placeholders, does not explain every permission the manifest declares, or has no usable screenshot (no image at all, or every local image path is missing on disk — a remote `https://` image passes here and is verified later by `preflight`); it also fails if `server/index.js` was never built. Failures that only the registry cares about do **not** block `trek-plugin pack`, so the dev loop keeps working while the docs are still a stub — but they do stop `publish`, which runs these same checks first and refuses before anything is packed, tagged or released. A handful of checks are *warnings* that never fail the command: directory name ≠ plugin id, an unbounded `trek` range, an unknown required addon, emoji in manifest text, an undeclared egress host, and a page/widget not using the design kit. This is a **subset** of registry CI — CI additionally verifies the release tag/commit, the artifact's sha256, and the README over the network. A local pass predicts a CI pass. |
|
||||
| `trek-plugin validate [dir]` | Manifest + layout checks, and the gate for publishing: parses the manifest with the same rules as install, then runs every registry gate that can be answered from the working tree — exiting non-zero on any of them. It **fails** if `README.md` is missing, is missing one of the four required sections, has too little prose, still holds template placeholders, does not explain every permission the manifest declares, or is missing `docs/screenshot.png` (the exact file the store card loads — README image links pointing anywhere else do not satisfy the gate; `trek-plugin shot` writes it, and `preflight` re-verifies it at the pinned commit); it also fails if `server/index.js` was never built. Failures that only the registry cares about do **not** block `trek-plugin pack`, so the dev loop keeps working while the docs are still a stub — but they do stop `publish`, which runs these same checks first and refuses before anything is packed, tagged or released. A handful of checks are *warnings* that never fail the command: directory name ≠ plugin id, an unbounded `trek` range, an unknown required addon, emoji in manifest text, an undeclared egress host, and a page/widget not using the design kit. This is a **subset** of registry CI — CI additionally verifies the release tag/commit, the artifact's sha256, and the README over the network. A local pass predicts a CI pass. |
|
||||
| `trek-plugin preflight --repo <o/n> --tag <vX>` | Runs the **full** registry CI checks locally over the network (tag→commit, manifest parity, artifact sha256/size, native scan, README quality gate) against your pushed release — so you catch a CI failure before opening the PR. |
|
||||
| `trek-plugin submit --repo <o/n> --tag <vX>` | Opens the registry PR for you: forks TREK-Plugins, fast-forwards the fork, branches off the registry's current main, writes/merges `registry/plugins/<id>.json`, pushes, and creates the PR. Requires `gh`. |
|
||||
| `trek-plugin publish --repo <o/n> --tag <vX>` | **The one-command release**: local gates → pack → tag + GitHub release → preflight → open the registry PR. Nothing is packed, tagged or released until the local gates pass (`--no-checks` skips them). A failure after the release is cut rolls back the tag and release that run created, so the same tag can be re-used (`--keep-release` opts out). Add `--sign` to sign it; `--sign --allow-key-change` rotates your signing key as part of the release. Requires `git` + `gh`. |
|
||||
|
||||
Reference in New Issue
Block a user