Official

settings

Built-in settings UI (JavaScript UI plugin).

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/settings@0.2.0

Permissions in 0.2.0

  • Manage plugins plugins.manageOfficial onlyReloads and enables plugins. Only official plugins can have it.List, enable, disable and reload plugins, install updates, and quit to finish an update
  • Change app settings settings.writeHighChanges the app settings.Change the settings and the theme
  • Manage agents agents.manageHighEnables, disables and signs in agents, and changes their settings.Turn agents on and off, sign in to them and update them
  • Control chats chats.controlHighCreates chats, sends prompts and cancels runs.Import an agent's own sessions as chats
  • Read chats chats.readMediumReads your transcripts and chat lists.List the workspaces an agent's sessions can be imported into
  • Read app settings settings.readLowReads the app settings.Show the settings, and the tools and rules of every plugin
  • Show panels ui.slotsLowShows views in the listed parts of the window.Draw the settings pageSlots: center

Files

views/updates.ts3.4 KB
// The Updates section: the app's own update, the default plugins' build,
// and when they were last checked. Install restarts when it must.
// HTM whitespace is significant; keep these templates out of Prettier's HTML formatter.
import { html as htm } from "convergence";
import type { EffectViewContext } from "convergence/effect";
import type { SettingsPage } from "../controller.ts";
import { card, notes, section, setting, column } from "./layout.ts";

function when(iso: string | null) {
  if (!iso) return "never";
  const minutes = Math.round((Date.now() - Date.parse(iso)) / 60000);
  if (minutes < 1) return "just now";
  if (minutes < 60) return `${minutes} min ago`;
  const hours = Math.round(minutes / 60);
  return hours < 24 ? `${hours} h ago` : new Date(iso).toLocaleDateString();
}

export function renderUpdates(ctx: EffectViewContext<object>, actions: SettingsPage["Service"]) {
  const u = actions.state.updates;
  if (!u) return htm`<empty>Loading…</empty>`;
  if (!u.enabled) {
    return section(
      "Updates",
      null,
      card([
        setting({
          title: `Divergence ${u.appVersion}`,
          description:
            "This copy runs its plugins from a source tree, so it updates from the repository, not from releases.",
        }),
      ]),
    );
  }
  const busy = !!u.busy;
  const app = u.appUpdate;
  const plugins = u.pluginUpdate;
  const current = u.plugins
    ? `Build ${u.plugins.build}, from Divergence ${u.plugins.version}.`
    : "Not from a release.";
  const downloading = u.busy === "downloading the update";
  return htm`
      <div ...${column} gap="large" padding="small">
        ${section(
          "Updates",
          u.error ? `Last check failed: ${u.error}` : `Checked ${when(u.lastChecked)}.`,
          card([
            setting({
              title: app ? `Divergence ${app.version} is available` : `Divergence ${u.appVersion}`,
              description: app
                ? `You have ${u.appVersion}. The app restarts to finish the update.`
                : "This is the newest version.",
              control: app
                ? htm`<button id="install-app" label=${downloading ? "Downloading…" : "Install and restart"} icon="circle-arrow-up" small variant="primary"
                  loading=${downloading} disabled=${busy} onClick=${() => ctx.run(actions.installApp())} />`
                : htm`<button id="check-updates" label="Check now" icon="rotate-cw" small variant="outline"
                  loading=${u.checking} disabled=${u.checking || busy} onClick=${() => ctx.run(actions.checkNow())} />`,
            }),
            app ? notes(app.releaseNotes) : null,
            setting({
              title: plugins ? `New default plugins (build ${plugins.build})` : "Default plugins",
              description: plugins
                ? "Plugins you have not changed update at once. For the ones you changed, an agent opens in the Plugins workspace and merges the update into your version."
                : `${current} Up to date.`,
              control: plugins
                ? htm`<button id="update-plugins" label="Update plugins" icon="circle-arrow-up" small variant=${app ? "outline" : "primary"}
                  loading=${u.busy === "updating the plugins"} disabled=${busy} onClick=${() => ctx.run(actions.applyPlugins())} />`
                : null,
            }),
            plugins ? notes(plugins.releaseNotes) : null,
          ]),
        )}
      </div>`;
}

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <324.7 KB7 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.