Repository navigation
Conversation
Contributor
|
Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies. |
Contributor
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This change spans a substantial new plugin execution platform with subprocesses, persistence, authorization, secrets, MCP tools, npm installation, and user-facing web, desktop, and mobile behavior. It also changes default capability flags and adds production static-analysis suppressions, so the scope and sensitivity require human review. You can add or adjust custom eligibility rules. Learn more. |
📝 Walkthrough
|
Stacked on #16055 (and #15010). Review only the top commit: cc1f8c5.
Problem
A plugin can declare settings, such as an API URL or an API token, but the only way to fill them in is a raw
plugins.settings.updaterequest from an administrative connection. A user who has just approved a plugin on the Plugins page has nowhere to give it its token.This PR adds a settings form for each installed plugin that declares settings, on web and desktop. Mobile shows the saved values read-only.
Why this qualifies
This is the proposal route in CONTRIBUTING, and no maintainer has agreed to it yet. It needs the plugin-system approval on #6714 / #6837, like the plugin PRs below it.
It stacks on the plugin management UI PR and uses its access rule, so a form is editable exactly when the Plugins page is. It uses the settings RPCs from the plugin settings PR and adds no server code, no RPC and no scope. If the answer is no, we close this with the plugin PRs around it. Previous PR in this stack: feat(web,mobile): manage plugins in Settings (#16055).
Fix
Shared client state (
packages/client-runtime/state/pluginSettings): subscribes to one installation's saved values on servers with thepluginSettingscapability, and saves throughplugins.settings.update. It turns each declared field and its saved value into a form row, and checks a draft against the field (type,min/max/integer, options, length) before anything is sent. Secrets are write-only: a row knows only whether a secret is saved.Web and desktop: under Settings > Integrations, each plugin in the selected environment that declares settings gets a section with its form: text and number inputs, a select, a switch, and a secret input whose placeholder says a value is saved. Reset returns a field to its default, and Clear deletes a secret.
Mobile (read-only): the environment's settings screen lists each plugin's declared settings and the value the plugin reads (a default is marked "(default)", including when a saved value no longer fits the field after an update and the plugin falls back to the default; a secret shows only "Saved" or "Not set"), followed by "Plugin settings are view-only on mobile. Edit them from an administrative web or desktop connection." There are no inputs or save paths on mobile: the mobile app always pairs with standard scopes (unchanged from main), so it can never have the
access:writesaving needs.Read-only: saving needs
access:writefrom the current session read, the same rule as plugin management. Denied, unreadable or still-checking sessions see the values read-only. Every save path refuses at dispatch, not only through disabled controls: submit, reset or clear, a choice and a switch. The form checks its current state insave.Docs: the settings section of
docs/user/plugins.mdnow says where the forms are, how saved secrets show, that a standard pairing sees values read-only, and that mobile shows them read-only.Size: 20 files, +1056 / −5. About 0.4k of the added lines are tests.
Evidence
Environment: macOS arm64; this PR on top of the plugin management UI PR.
How to exercise it: use an isolated
vp run devwith a scratch plugin whose manifest declares atext, anumber, aselect, abooleanand asecretfield. Approve it on the Plugins page, then open Settings > Integrations. Then pair a second client with a standard link, and open the environment's settings screen on mobile.Captured in isolated real clients: web (Chromium, with WebSocket frames counted), the built desktop app (its own isolated profile and bundled server), and one
vp run dev --shareremote browser; web and desktop forms are unchanged at this head. Android was captured at this head on an emulator with a standard pairing (the only kind mobile gets), after values and the secret were saved from an administrative web session. In both revisions the fixture was enabled on the Plugins page (before).On Android the values are plain read-only text under "Plugin settings are view-only on mobile. Edit them from an administrative web or desktop connection." Saved values show as set (Retries "3"); unset ones fall back to the manifest default with a suffix (API URL "https://api.example.com (default)", Verbose logging "Off (default)", Mode "Safe (default)"); the secret shows only "Saved", never its value. The plugin's own details screen has no settings section in either revision (before, light, dark). The before environment screens were taken at an earlier revision of the parent whose mobile environment screen is identical to the current parent's.
plugins.settings.updateVideos: edit, save, reload · invalid value, secret, Clear, Reset · desktop · remote · before: web, desktop.
Remote: an owned
vp run dev --shareserver and a fresh browser over its HTTPS origin. An administrative pairing saved and reloaded a value; a standard pairing saw it read-only and sent nothing.Checks at this head (
b88b49899d), re-run 2026-10-05 (CI=true, all exit 0):vp test run src/state/pluginSettings.test.ts src/state/pluginPresentation.test.ts: 2 files, 45 tests pass. They cover rows from declared fields, saved values and the secret flag (and whether the value shown is the default, including a saved value that no longer fits), drafts checked against the field, capability gating, and the read-only rule.vp test run src/features/plugins/PluginSettingsValues.logic.test.ts: 3 tests pass. With nothing saved the values read "2 (default)" and "Safe (default)"; saved values that still fit read "4" and "Fast" with no label; a saved 9 after the range became 0–5, and a select value whose option was removed, read "2 (default)" and "Safe (default)". With the shared row treating any saved value as not a default, that last test fails (recorded during development).vp test run src/components/plugins/PluginSettingsForm.test.tsx: 2 tests pass. It drives the real form through an edit, a submit and Clear. A standard pairing's form sends noplugins.settings.updateby any path. An administrative form sends exactly one save with the edited value.vp test run src/auth/RpcAuthorization.test.ts src/plugins/PluginCatalogRpc.test.ts src/plugins/PluginSettingsRpc.test.ts: 3 files, 17 tests pass, including a standard session deniedplugins.settings.update. Mobilesrc/dependency-graph.test.ts: 3 pass.vp run --filtertypecheck for contracts, client-runtime, web and mobile;vp lint --report-unused-disable-directivesandvp fmt --checkon the added and modified files (no warnings);vp run knip:check;vp run lint:mobile.Not part of the re-run (no server, desktop or packaging change):
vp run --filter @t3tools/web build,vp run build:desktopandnode scripts/release-smoke.tspassed at the top of this stack (the example plugins PR), which contains this change.Earlier runs, not repeated at this head: on an earlier revision with the same client-runtime and web tests, both test files failed to load on the parent (modules missing), and with the web
saveguard removed both web tests failed. The mobile editor and its logic tests from that revision are gone.Surfaces
PluginSettingChangetype and the two input length limits (PLUGIN_SETTING_TEXT_MAX_LENGTH,PLUGIN_SETTING_SECRET_MAX_LENGTH) are exported again for the forms.docs/user/plugins.mdupdated. No internals doc.Not verified
apps/mobile/src/connection/platform.ts, unchanged from main), so mobile has noaccess:write; this PR ships a read-only list there and changes no scope.Claude Opus 5.5 (build), GPT-6.1 Sol (review) and GPT-6 Astra (captures) via T3 Code
🤖 Generated with Claude Code