c53f699714
- go plugin backends ship as release tarballs (per-platform binaries, checksums) that kandev installs from url/upload or filesystem sideload+sync, then spawns and supervises via hashicorp/go-plugin (unix socket / loopback tcp+automtls) - strict typed grpc contract (kandev.plugin.v1): DeliverEvent/InvokeTool/HandleWebhook plus a capability-gated host service (state, secrets, emit); author sdk in pkg/pluginsdk; packaging cli in cmd/plugin-pack - native frontend bundles: window.registerKandevPlugin + registry/host loader with dynamic routes, nav items, slots and ws handlers; plugins settings page with install dialog, sync button and enable/disable/uninstall - gated behind the plugins feature flag (prod off, dev/e2e on); covered by unit and integration tests plus playwright e2e against a real packaged fixture plugin Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
44 lines
1.8 KiB
TypeScript
44 lines
1.8 KiB
TypeScript
/**
|
|
* Boots the plugin host from the app's boot payload
|
|
* (docs/plans/plugins/PLUGIN-API.md). Called once from the app
|
|
* bootstrap once the store + boot payload are both available.
|
|
*/
|
|
import type { StoreApi } from "zustand";
|
|
import type { AppState } from "@/lib/state/store";
|
|
import type { BootPayload } from "@/src/boot-payload";
|
|
import { buildHostApi } from "./host-api";
|
|
import { installPluginGlobal, loadPlugins } from "./host";
|
|
|
|
/** Store instances that have already had `loadPlugins` triggered — boot is a one-shot per store. */
|
|
const bootedStores = new WeakSet<StoreApi<AppState>>();
|
|
|
|
/** Reads the resolved light/dark theme applied to the document by AppThemeProvider. */
|
|
function readResolvedTheme(win: Window = window): "light" | "dark" {
|
|
return win.document.documentElement.classList.contains("dark") ? "dark" : "light";
|
|
}
|
|
|
|
/**
|
|
* Installs `window.registerKandevPlugin` and loads every plugin in
|
|
* `bootPayload.plugins`, building each one's `PluginHostApi` from `storeApi`.
|
|
*
|
|
* No-op when `feature` (the `plugins` feature flag) is off, or when the boot
|
|
* payload carries no plugins. Idempotent per `storeApi` instance — repeated
|
|
* calls (re-renders, StrictMode double-invoke, HMR remounts of the calling
|
|
* component) never re-install the global or re-import a bundle for a store
|
|
* that has already booted.
|
|
*/
|
|
export function bootPlugins(
|
|
bootPayload: Pick<BootPayload, "plugins">,
|
|
storeApi: StoreApi<AppState>,
|
|
feature: boolean,
|
|
): void {
|
|
if (!feature) return;
|
|
if (!bootPayload.plugins || bootPayload.plugins.length === 0) return;
|
|
if (bootedStores.has(storeApi)) return;
|
|
bootedStores.add(storeApi);
|
|
|
|
installPluginGlobal();
|
|
const theme = readResolvedTheme();
|
|
void loadPlugins(bootPayload.plugins, (pluginId) => buildHostApi(pluginId, storeApi, theme));
|
|
}
|