Packages and installs
Where plugins come from: npm, git or a local path, the Plugins page, and the default plugins as packages
A plugin is a package, as a pi package is. Ovvi installs it,
lists it in ~/.config/ovvi/settings.json, and can update, reload and
remove it. The app ships no package manager. It uses the npm, pnpm or bun on
the user's login PATH.
Sources
settings.json lists the packages the user installed, under packages. An
entry is a source string, or an object with a source (pi's form):
{
"packages": [
"npm:@acme/ovvi-csv@1.2.0",
"git:github.com/acme/ovvi-kanban@v1",
"~/code/my-plugin"
]
}| Source | What it is | Where it goes |
|---|---|---|
npm:<spec> | an npm package: npm:@scope/name@1.2.0, npm:name, or a tarball's path | ~/.config/ovvi/plugins/<name>/: npm pack, then the tarball unpacked. Needs npm |
git:<repository>[@ref] | a git repository: git:github.com/x/y@v1, an https:// or ssh:// URL, git@host:x/y, or a local repository's path | ~/.config/ovvi/plugins/<repository name>/: a clone at the ref |
a path that starts with /, ~ or . | a package on this Mac | nowhere: it loads in place. A relative path is from the folder of settings.json |
- A versioned spec stays at its version. Update fetches the same source again, so it gets the newest version that the spec allows.
- A local path is never installed or changed. Its dependencies are its
author's: run
npm installandnpm run buildin its folder. - A folder that the user or an agent puts in
~/.config/ovvi/plugins/by hand also loads. It is not insettings.json. - The repository's extra plugins, in
plugins/extra/(Tart VM), are local paths:npm ciandnpm run buildin the folder, thenovvi plugins install ~/code/ovvi/plugins/extra/<name>or the path in Settings ▸ Plugins. The app carries onlyplugins/builtin/. hideInNewPanelists the ids of the plugins that the New pane chooser leaves out, for example"hideInNewPane": ["git"]. A plugin's page in Settings and the confirmation write it.
Install
- The app fetches the source into a staging folder in the plugins folder. No script of the package runs.
- It reads the manifest and shows the confirmation: the id, the version, the source and folder, the description, and that plugins run code as you (Security and trust). Cancel removes the staging folder.
- It installs the package's
dependenciesanddevDependenciesinto the package's ownnode_modules, from its lockfile when it has one, with that lockfile's manager. Without a lockfile it uses the first of npm, pnpm and bun on thePATH. - If the package has a
buildscript and its built entry is missing, the app runsrun build. A published package that ships itsdist/needs no build. - It moves the package into place, over an older install, adds the source
to
settings.json, keeps its id intrust.json, and loads its open cards again.
The install commands:
| Manager | Command |
|---|---|
| npm | npm ci (with a lockfile) or npm install, with --no-audit --no-fund --ignore-scripts --legacy-peer-deps |
| pnpm | pnpm install --frozen-lockfile (or --no-frozen-lockfile), with --ignore-scripts --config.auto-install-peers=false |
| bun | bun install --ignore-scripts --omit=peer, with --frozen-lockfile when the lockfile is bun's |
- No install script of a dependency runs (
--ignore-scripts). Only the package's ownbuildscript runs, and only after the user confirms it. - No peer dependency installs.
@ovvi/plugin-apiis the app's, and it is not on the npm registry (Types). - A fetch (
npm pack,tar,git clone,git checkout) stops after 5 minutes, and an install or build command after 10 minutes. A failure shows its last 20 lines, and the plugin's log has the whole output. - An id that another package already has is refused: "The id ‹id› is taken by ‹folder›". So is a folder name that is already in the plugins folder.
The Plugins page
Ovvi ▸ Plugins… and ⌘K Plugins… open Settings at its Plugins page (Settings).
- The field takes a source (
npm:package,git:repositoryor a folder's path). Return or Install installs it. The spinner and "Installing…" take the Install button's place while it runs. A failure shows on a red notice, with Retry when running it again can help. - New Plugin… asks for a name, then makes a plugin from the template (Build a plugin).
- Reload All reads every package again and reloads every plugin with an open card. ⌘K's Reload Plugins does the same.
- A row per plugin, the browser first: its icon, its name and version,
and its source under it: "Comes with Ovvi", the
npm:orgit:source, the local path, or the folder. A source insettings.jsonwhose folder is missing shows "Not installed". A row opens the plugin's page. - An agent's work shows on the page too: an agent on This Mac makes,
reloads and installs plugins with the
ovvicommand (CLI), and the page shows Creating…, Installing… and any failure, as for its own.
A plugin's page
-
The header: the icon, the name, the version, the description and the source, and the buttons that apply:
Button On Does Update npm and git installs ("Install" while its folder is missing) fetches the source again. It does not ask again, unless the new version has another id Update to Version ‹x.y.z›, Reset to Default the defaults, when the app offers it The default plugins Show Log every installed plugin but the browser opens <id>.login the app for.logfiles (Console)Reload every installed plugin but the browser runs its build script and loads its cards again (Reload) -
Remove…, in a section of its own at the end of the page, "Remove ‹name›" (every plugin but the defaults): asks, then takes it off the list, takes its id out of
trust.json, forgets its settings, and moves its folder to the Trash. A local path's folder stays where it is. -
Its settings, drawn from the manifest's
settings(Settings). A change is saved at once and reaches the plugin's open cards. -
Show in New Pane: a switch for each plugin whose manifest has a
chooserrow. The install's confirmation sets it with its checkbox, which is off by default. Off, the New pane chooser leaves the plugin out, andsettings.jsonkeeps its id inhideInNewPane. ⌘K's Open ‹chooser› Here stays. -
The browser's page has its setting and Show in New Pane, and Show in Privacy for its approved websites (The browser).
Plugin settings in settings.json
settings.json keeps the values that the user set on the plugins' pages,
under plugins, by plugin id and setting id. Only a value that differs from
the setting's default is written, so a new default reaches a user who never
changed it:
{
"packages": ["npm:@acme/ovvi-csv@1.2.0"],
"plugins": {
"explorer": { "hidden-files": true },
"git": { "commits": 1000 }
}
}- A value that does not fit its setting (a wrong type, an option that is not there, a number out of range) reads as the default.
- Remove takes the plugin's values out. A hand edit, or an agent's edit, takes effect at Reload Plugins.
Where the registry looks
The registry reads the manifests at launch, and again after an install, an update, a removal and Reload Plugins. Nothing watches the folders. The first package of an id wins, in this order:
- The browser, whose manifest is written in Swift.
- The default plugins: their installed copies in the plugins folder, or the app's own manifest while a copy is not installed yet.
- The local paths in
settings.json. - Every folder in
~/.config/ovvi/plugins/but hidden ones andnode_modules.
A package that is left out (a bad manifest, a taken id, a listed path with no
package.json) is written to the log under the folder's name. A bad manifest
also goes under the manifest's id when the id reads.
The default plugins
File Explorer (explorer) and Git (git) are packages, each with its
package.json, lockfile, src/ and build script. The app carries their
source, without node_modules and dist, and nothing of theirs is in the
app's own build.
- First launch. When a default's folder is missing in the plugins
folder, the app copies its source there, runs
npm cifrom its lockfile, then its build script (about 1 s each). Its cards show "Installing ‹name›…" meanwhile. With no package manager, or offline, the card says what failed, with Try Again (Errors and logs). - They are the user's. The user or an agent can edit them, then press Reload Plugin. They never ask for a confirmation.
- An app with a newer version (the package's
version) replaces an unedited copy at launch. An older app never replaces a copy, so the installed app and another Release build (make app) can share the folder. - An edited copy, or a copy whose source differs from the app's at the same version, stays. Its page offers Update to Version ‹x.y.z› (a newer version) or Reset to Default. For an edited copy it asks first: "Replace your edited ‹name›?". The edited copy goes to the Trash.
.defaults.jsonin the plugins folder keeps each default's version, the hash of the app's source, and the hash of the copy after its install. A hash covers the files outsidenode_modulesanddist, so a build does not count as an edit.- A quit during an install leaves a
.install-*staging folder. The app removes it at the next launch.
Choices
The decisions are D96 and D100 on the Decisions page.