Navigace
ELOWEN · Dokumentace

Poslední aktualizace: 20. října 2026

Procházet dokumentaci · Installation, versioning and changelog

Tento článek zatím není přeložen. Je dostupný v angličtině.

Developer reference

Installation, versioning and changelog

Installation from the licensed catalog

The marketplace catalog is an allowlist served by the licensing server. Installation accepts a plugin name present in the catalog, not an arbitrary URL or local folder. The administrator opens the plugin marketplace, reviews the manifest metadata and compatibility requirements, and starts installation.

The host fetches the catalog over HTTPS with the installation licence token (revalidated with an ETag, falling back to the last-good cache when offline). The locally evaluated licence plugin set filters what the catalog shows: a plugin outside the licence is hidden, and installing or updating it is refused the same way the server refuses it. Each payload is a tarball pinned by the catalog's sha256 and size, verified while streaming; the archive listing is validated before extraction (regular files and directories under the plugin name only, no symlinks or escapes), and the payload manifest name and version must match the catalog entry. Catalog update status compares the installed manifest version with the validated catalog version, even when an older payload remains cached. Listing downloads no payload; candidate preparation verifies its exact pin. After parsing, loader, marketplace staging and update preflight call validateManifestFolder(manifest, name, dir) in src/plugins/manifest.ts. It requires the manifest name to equal the logical plugin folder name and returns the resolved entry path only when it stays lexically within the directory. The expected name is explicit because a staging directory can have a different basename. This check performs no filesystem I/O and does not parse the manifest again. A mismatch or escape throws before import or swap. Marketplace staging separately rejects symlinks and bounds file count and bytes; preflight separately resolves real paths and requires both plugin and declared service entries to be files inside the plugin tree. Preflight keeps the injected target-core parser and checks the effective enabled baseline plus every replacement.

The marketplace gates the downloaded candidate's consent and control dependencies before queuing the pinned version and sha256. It rejects built-in plugin names, unsafe names, malformed catalog entries, excessive plugin trees, a newer requiresCore or an incompatible requiresSharedApi. A missing required control returns the existing dependency error naming it and available providers. A consent refusal changes no executable files; confirmation replays the same install or update with the returned acknowledgeGrants.

POST /plugins/marketplace/:name/install with optional enable and acknowledgeGrants, POST /plugins/marketplace/:name/update with optional acknowledgeGrants, and POST /plugins/marketplace/:name/repair with an empty body return 202 { requestId, queued: true }. Install enables by default; enable:false still uses the same restart and proof transaction but needs no enablement consent. Any request also advances every other enabled plugin with a newer version in the same run, so a shared-API bump moves the whole stale set at once; disabled plugins advance when they are enabled. POST /plugins/marketplace/update with an optional acknowledgeGrants map keyed by plugin name queues the update of every stale plugin in one request and returns 202 { queued: true, plugins }.

Only the independent update coordinator stages and replaces publisher-plugin code. It shares the core transaction: preflight with the target parser, stop the daemon, web and affected independent plugin sockets/services, record observed backups and the enablement delta, swap, start, prove, then publish receipts or roll the whole set back. Disabled plugins are proven by tree inventory; enabled plugins and their independent service starts must converge. There is no per-plugin pending-install record or daemon-side settlement. Bundled removal persists disablement and soft-removal together with cancellation of queued plugin work inside one plugin mutation lock. User uninstall cancels queued work before awaiting gateway and service shutdown.

Installed publisher identity is the receipt plus an unchanged tree, even when the running manifest parser rejects it. Such a plugin remains Installed with its receipted version and degraded reason, and Repair submits an explicit retry. Boot detects missing or unloaded enabled receipted plugins after registry construction and queues repair without changing files or appending journal intents. It never repairs customer plugins or queues during an active non-terminal journal. A version that failed activation is not repaired automatically again; offline, licence and consent refusals do not consume that activation guard.

Admin GET /system/update is the status authority for the queued request: { queue, journal, report }. The journal field is null once the journal reaches a terminal state. Match the request id against journal operations or the report's requests and per-plugin requestId; acceptance alone is not proof. Each per-plugin outcome is one of applied, rolledBack (with a detail), needs-consent, not-licensed, superseded, failed or skipped. The report state needsOperator marks a result that needs operator attention. The coordinator rechecks consent and renewed entitlement; a catalog pin that moved is superseded rather than replaced with different bytes.

Automatic selection plans against the core the transaction installs, or the running source core for plugin-only runs. Catalog version pins the offered release and must match the payload manifest; the description is publisher display metadata. A candidate whose requiresCore exceeds the effective target is deferred as needs-newer-core. Unchanged baseline plugins, including customer plugins, must parse on the target before services stop.

Versioning

Keep these axes separate:

  • The daemon version is the core package version. It is the version compared by requiresCore for a manual install; an automatic update compares the core its transaction installs instead.
  • The plugin's version is its own release version. Bump it whenever installed bytes change.
  • apiVersion is the exact breaking plugin API contract.
  • requiresCore is a minimum core version checked by registry installation.
  • requiresSharedApi is an exact shared-helper contract checked before the entry is imported and during installation.
  • web.requiresApiVersion and the browser registration's requiresApiVersion target the browser runtime contract, currently 66 (PLUGIN_UI_API_VERSION in packages/plugin-ui-kit/index.js).

A new observational hook is additive: plugins that use it raise requiresCore, not apiVersion (stays "2") and not the browser API (the PLUGIN_UI_API_VERSION axis, unrelated).

Do not use plugin version fields to communicate API compatibility. Update the plugin manifest and registry catalog together.

An artifact patch that only raises the plugin-API floor does not need an in-app note; a release ships one only when something changed for the people using Elowen.

Shared API 14 declared API patterns

Shared API 14 first shipped with package 0.11.0 and core 0.30.4. Package 0.12.0 retains that exact API number while adding exported retry and retry-presentation helpers, localized shared messages, typed host-condition errors and local-execution classifiers. UI kit 0.24.0 publishes UI API 67 and depends on shared 0.12.0. An API 14 match does not guarantee these new subpaths on an older package; build and validate against the matching core. The core release command assigns the artifact patch; package metadata is not proof that a matching core artifact has been built or released. Namespaced and root-mounted authenticated routes match only their complete declared patterns. Replace remainder parsing with declared :name segments and read req.params, which the host decodes once. List and item routes need separate declarations and registrations. The catalog reports real patterns without a prefix marker. Public /hooks routes retain their existing prefix behavior.

Every bundled, registry and external plugin must pin requiresSharedApi: 14. The exact gate refuses API 13 before entry import, even for plugins without routes. Patch each registry artifact and update its manifest, catalog, README and test pins. Update core and the loaded plugin set together through the existing coordinator; there is no compatibility routing path.

Earlier shared-API releases

The shared-API number advanced release by release from 10 (shared package 0.7.0) to 14 (0.11.0); 0.12.0 keeps 14 and only adds helpers. Each step was a coordinated release in which bundled, registry and external plugins moved their pin in the same update, and the exact gate refused the previous number before the entry was imported, whatever the artifact's requiresCore floor said.

The contracts those levels introduced are part of API 14 and are documented where they are owned: the awaited database seam, schema-qualified migrations, transaction-local handles and the draining helpers in Persistence and caches, and the host stores, project files, ctx.timezone() and the image runtime in PluginContext and workflow results.

An installed plugin pinned below 14 is refused until it is rebuilt against the matching shared package and installed with its paired core in one coordinator transaction; the loader carries no aliases or adapters for older numbers. Change the artifact patch of every affected plugin and keep its manifest, registry catalog and README metadata identical. A registry check uses the selected candidate checkout's shared package and SDK; when that checkout has no current built SDK the gate stays blocked, because another checkout's dist cannot validate the candidate.

Shared API 13 coordinated PostgreSQL release

API 13 introduced the awaited database seam, schema-qualified migrations and transaction-local handles. These contracts remain part of API 14; their current owners are linked above.

Shared API 12 history

API 12 made StateStore writes asynchronous. Callers await committed writes; this contract remains part of API 14.

Shared API 11 coordinated release

API 11 moved binary Project transfers to the host Project-files seam and image runtime. The current contracts remain in API 14.

Shared API 10 coordinated release

API 10 removed earlier configuration-migration, embedding and checkpoint contribution paths. Current plugins use their owning host seams and the API 14 gate, not those retired paths.

Writing a changelog entry

The in-app What's new page (/p/changelog) is for people using Elowen, not for developers. Only a release with a big UI change or a new feature, something a user or an administrator can be shown in the app, gets a short, dated update; most releases get none. Bug fixes, polish, relabels, speed-ups and technical detail belong in CHANGELOG.md and the developer docs, never here.

While a release is being developed, write its note as plugins/changelog/entries/unreleased.md (English) with unreleased.cs.md and unreleased.sk.md beside it, and commit all three. The page never shows them. npm run release:prepare produces one entry per calendar day, in <version>.md, <version>.cs.md and <version>.sk.md. If that day already has a patch entry, it appends the new content in every language, keeps the day's title and advances its file name and version to the new patch. This makes the appended content unread even for readers of the earlier patch. The product-line overview is a separate entry of its own (0.29.md), read by the same parser. The release commit names the note files it consumed and the entries it renamed. A release without an unreleased note ships no update; a note missing a translation, or one that was never committed, stops the release before anything changes.

---
title: The agent sees what each click did
---

One or two sentences: what this update means for the reader.

### New

- **Short name.** One sentence: what you can do now. Where: [Settings → Models → Model roles](/settings?cat=models)

### Improved

- **Short name.** What works better, faster or more simply, and where.

### Changed

- **Short name.** What behaves differently, or what the reader has to do.

:::admin
- Only administrators receive this part: installation, updates, licences,
  plugin compatibility, migrations and backups.
:::

Plugin-specific sections or bullet groups use the same container syntax as administrator notes:

:::plugin sandbox
### Stop a desktop

Stop a desktop from Account while keeping the Project running.
:::

:::admin
:::plugin discord telegram whatsapp
### Voice messages

Configure voice for your connected chat apps.
:::
:::

Names are manifest plugin ids, separated by spaces. Multiple names mean any of those plugins makes the item applicable; the page shows pills only for plugins this account can use, carrying the plugin's display name and no icon. Admin and plugin containers may nest, but plugin containers may not nest inside each other. Use a heading or a bold bullet title inside each plugin container, because the page builds the pill row from that title with the shared Badge; an item without one fails to render. Keep the same scopes and audience in cs, sk and en. Untagged core content remains public. The daemon checks the live core access predicate, including installed/loaded, enabled and per-account grants, for admins too. Filtered items never leave the daemon. A note with no remaining content disappears from its listing and unread badge; headings alone are not content. Unclosed containers retain restrictions to the end and log a warning; malformed or stray container markers fail loading.

Every section is optional; keep this order. Name places in the app with the labels the web dictionaries and plugin strings use, as an in-app link when the place has a URL (/settings?cat=models, /p/editor) and as a bold click path otherwise. Do not list plugin versions: write a plugin's change as an ordinary item in the matching section. Translations are natural Czech and Slovak with formal address, not word-by-word copies.

The page shows each update by its date and title. Administrators see the release version as a tooltip on the date; nobody else sees version numbers. The :::admin container is cut out on the daemon: a non-administrator is never sent its text, and an update whose only content is administrator notes is not listed or counted as unread for them. A container left open runs to the end of the note, so a missing ::: hides text rather than showing it to everybody. A link to a path inside the app (starting with /) opens through the app's own navigation; other links behave as ordinary links.

The parser is intentionally a small flat front-matter parser, not a general YAML parser:

  • It strips an optional BOM and recognizes a front-matter block only when it starts with ---, has flat key: value lines, and ends with ---.
  • Values in brackets become comma-separated arrays with surrounding quotes removed. The literals true and false become booleans. Other values are strings with one pair of surrounding quotes removed.
  • version is required and must be non-empty. Entries without it are skipped with a warning, except the unreleased files, which are skipped silently. Duplicate English versions are skipped.
  • date, title, tags, and pinned are optional. The parser does not validate the date format. tags is an array only when the value uses bracket syntax. pinned is true only for the literal true.
  • The English file owns date, tags, and pinned. A recognized translation contributes only its title, body, administrator section and plugin-scoped items. A translation without an English original is skipped, as is a duplicate translation of the same language and version. Missing translations fall back to English.
  • Only cs and sk filename suffixes are translations. Other Markdown files are treated as English candidates.
  • Entries sort with pinned entries first, then by descending numeric version. Every segment counts and a missing one reads as zero, so the line overview 0.29 sorts below the update 0.29.54. Text after the first hyphen is ignored, and a segment that does not start with digits sorts as zero.
  • The body is trimmed Markdown. Unknown front-matter keys are ignored.
  • Release assets belong under entries/assets/<version>/, which the plugin serves at /plugins/changelog/api/asset/<version>/<file> for authenticated users. The asset route accepts only png, jpg, jpeg, gif, and webp, and rejects SVG. An image written as a relative assets/<version>/<file> keeps that folder in its URL, so an append that advances the entry's version still finds the images its author committed; any other relative source resolves under the entry's own version. The route answers immutable for a year, so GET entries/<version> also returns assets, a content fingerprint per shipped image keyed <version>/<file>, and the page adds it to the image URL as ?v=<fingerprint>: an image reworked under the same name gets a new URL instead of the old bytes a cache in front of the daemon still holds.
  • The page uses the entry version to calculate each account's unread state. Opening the page records the newest visible update, so every later update is new to that account. An admin can reset an update to unread for all accounts.

Entry translation languages

plugins/changelog/lib/entries.mjs owns isEntryTranslationLanguage(lang), the shared boolean predicate for translated entry filenames and API locale normalization. It accepts cs and sk; English uses the original Markdown entry. The translation-language set remains private. The filename parser applies the predicate to its extracted suffix, and registerRoutes applies it directly to the requested locale. An absent, unsupported or unavailable translation uses the English original. The predicate performs only a membership lookup and has no I/O or registration cost.

Owning code and limits

  • Parser and visibility rules: plugins/changelog/lib/entries.mjs. Routes, asset serving and the per-account marker: plugins/changelog/lib/api.mjs. Release preparation: scripts/release-prepare.mjs, which runs the same parser for the day's merge.
  • Minimal use: write unreleased.md with its cs and sk translations and commit all three. The release procedure then turns them into the day's entry; no other plugin code is involved.
  • Absence: a release without a note ships no update, so the page lists no entry for that release.
  • Limits: the parser is a flat front-matter reader, not YAML. Container markers must be exact, and a malformed one stops loading. Assets are limited to the five raster types above.
  • Cost: entries are parsed once per boot, so a note change needs a daemon restart, and the page reads the account's marker only.