Publish
From a plugin's folder to the Install button of every Hodi: test, try on a test Hodi, build the file, then have it checked and signed through the plugin registry.
In short
A plugin goes through four stages before it reaches people. The first three are done alone, on one's own machine. The last one goes through Hodi's maintainer.
This stage checks the plugin's server part, without needing a Hodi. The command to run is npm test.
This stage installs the plugin on a test Hodi, every time a file is saved. The command to run is npx hodi-plugin dev.
This stage makes the .hodi-plugin file, the file Hodi installs. The command to run is npm run build.
During this stage, Hodi's maintainer reads the plugin's code and signs the .hodi-plugin file. This stage goes through a pull request.
The plugin template already has everything: the SDK, the scripts and the CI. There is nothing more to install.
Test
What a test does
A test runs the plugin's server part the way Hodi runs it:
- The SDK builds the plugin.
- It loads it in the same sandbox as Hodi.
- It calls its routes with the same checks as Hodi.
Around it is a small Hodi in memory: a household, its admin, and stand-ins for sites, e-mails, notifications and the AI. So a test never sends anything to anyone. It runs the same way on one's machine and in the CI.
An example
The plugin template already has this file. It asks for the POST /hello route and looks at the answer:
import assert from 'node:assert/strict'
import { after, describe, it } from 'node:test'
import { testPlugin } from '@hodi-app/plugin-sdk/testing'
describe('my-plugin', async () => {
const hodi = await testPlugin('.', {
settings: {
greeting: 'Hi'
}
})
after(() => hodi.stop())
it('answers a child too', async () => {
const child = await hodi.addPerson({
name: 'Zoe',
kind: 'child'
})
const answer = await hodi.route('POST /hello', {
as: child
})
assert.equal(answer.status, 200)
})
})To run the tests:
npm testThe template's CI runs them again at every push. A plugin that passes its tests behaves the same once installed in a real Hodi.
testPlugin's options
testPlugin('.') takes the plugin's folder. The second argument, optional, prepares the small Hodi:
settingsobjectThe settings, as the admin would have chosen them. The others keep their default.
sitesobjectThe answer of each site in permissions.network, by address. Any other address fails, like a closed site.
articlesobjectThe articles Hodi knows, by id, for ctx.articles.
ai(question) => stringThe AI's answer to each question. Without this option, the household has no AI.
mailbooleanE-mail is set up on the server (true by default).
householdobjectThe household's name (name) and language (language).
unsignedbooleanThe plugin is built unsigned. Hodi then refuses what it would refuse for real.
What the small Hodi offers
route('POST /hello', { as, body, query })Calls a route of the plugin. Without as, the household's admin calls.
addPerson({ name, kind })Adds someone to the household: adult, teen or child.
click(action, input)Plays a click on the plugin's AiButton.
runTask(name)Runs a task, as Hodi would at its time.
readFeed(key)Reads a feed of the plugin, like a household that follows it.
exportData(person)Asks for a person's copy of their data.
mailsThe e-mails the plugin sent, in order.
notificationsThe notifications the plugin sent, in order.
questionsThe questions the plugin asked the AI, in order.
logThe plugin's log lines, and Hodi's lines about it (a refused permission, for example).
stop()Stops the small Hodi at the end of the tests.
Try on a test Hodi
A test checks the server part. To see the web part too, a real Hodi is needed: Developer mode installs the plugin on it, then updates it at every save.
What is needed
- A Hodi of one's own, on one's machine for example (
http://localhost:3000). - Being its server's admin.
A test Hodi keeps the trials apart from one's household's data.
The steps
- Open Server › Plugins on the test Hodi.
- Turn on Developer mode: Hodi shows a code, only once.
- Copy this code.
- In the plugin's folder, run the command below, replacing
THE_CODE. - Leave the command running while working.
npx hodi-plugin dev --server http://localhost:3000 --code THE_CODE--serverThe test Hodi's address.
--codeDeveloper mode's code. It can also come from the HODI_DEV_CODE variable, so it doesn't stay in the terminal's history.
The plugin's folder, if the command isn't run from that folder.
What happens next
At every save of a file, the command:
- builds the plugin again;
- sends it to the test Hodi;
- waits for Hodi to restart with the new version (a few seconds);
- shows the plugin's log in the terminal.
In Hodi, the plugin wears the In development badge. Its pages show even unsigned, and it keeps its data from one version to the next. Ctrl+C stops the command.
Build
The plugin's file
npm run buildThe command writes a single file, dist/<id>-<version>.hodi-plugin. For example dist/hello-world-0.1.0.hodi-plugin. This is the file people install.
Installing it by hand
Any admin can install this file, even on a server they only manage from their browser:
- Open Server › Plugins.
- Click Add a plugin and choose the file.
- Read what Hodi shows: the name, the version and the permissions.
- Confirm the install.
What the admin sees before installing
Hodi looks at whether the file is signed, and tells the admin:
WhenThe file is signed by the key of Hodi's maintainer.
What happensThe plugin installs with everything it asks for.
WhenThe file isn't signed, or is signed by another key.
What happensThe plugin installs after one more confirmation. Its pages don't show.
WhenThe file isn't signed and asks for a permission kept for signed plugins.
What happensHodi refuses to install it.
The permissions kept for signed plugins are listed in The manifest.
Getting verified
Why a signature
A signature tells Hodi who made a plugin. Hodi trusts only one key: its maintainer's.
A plugin's pages run in Hodi's page, with the session of the person signed in. That is why only a signed plugin, or the one in development, can show its pages.
The steps
- Put the plugin in a public GitHub repository.
- Publish a release
v<version>(for examplev0.1.0) with its.hodi-pluginfile. - Add the plugin to the plugin registry with a pull request.
- The maintainer reads the plugin's code.
- If it follows Hodi's rules, they sign this version and accept the pull request.
In the plugin template, pushing a v0.1.0 tag is enough for step 2: the CI builds the file and creates the release on its own.
The registry entry
The registry is a plain list, the plugins.json file. An entry needs two fields, and the registry's npm run index command fills the rest from the release:
{
"repo": "https://github.com/me/hodi-plugin-hello-world",
"version": "0.1.0"
}The registry's README details the commands to run before the pull request.
Then
- The plugin shows in Server › Plugins › Available of every Hodi.
- It installs from there in one click.
- Each new version goes through a new pull request.
That new version is read, then signed in turn.
Hodi's rules
The maintainer checks each of these rules before signing a plugin.
AiButton asks the AI, once per click.The AI costs the household money: nothing may start it behind its back.
Hodi is bilingual, and speaks to everyone the same way.
The plugin looks like the rest of Hodi, in all three themes.
Everyone can get or delete what Hodi knows about them.
A Hodi that already installed a version must be able to move to the next one.
A published key would let anyone sign in its author's place.