Browser SDK for calling Mission Control plugin operations.
pnpm add @flanksource/plugin-ui-sdkCreate a Mission Control plugin client, then create an instance for a specific plugin/config pair:
import { createMissionControlPluginClient } from "@flanksource/plugin-ui-sdk";
const pluginClient = createMissionControlPluginClient({
mode: "proxy",
baseUrl: "/api/mission-control",
});
const kubernetes = pluginClient.New("kubernetes", "config-123");Creates a plugin instance scoped to a plugin ref and optional catalog config id. The instance exposes the operation methods.
Calls a plugin operation and returns the native Response.
const res = await kubernetes.invoke("list-pods");
if (!res.ok) throw new Error(await res.text());
const rows = await res.json();With params/body:
const res = await kubernetes.invoke("create-pod", {
namespace: "default",
name: "nginx",
image: "nginx:latest",
});Behavior:
- Defaults to
POST /api/plugins/:pluginRef/invoke/:operation. - Set
options.proxy: trueto use/api/plugins/:pluginRef/proxy/:operationinstead;pluginRefcomes frompluginClient.New(pluginRef, configId)and the HTTP method comes fromoptions.method. - Sends the instance
configIdas theconfig_idquery parameter. - Sends
{}when no body is provided for methods that support a body. - For
GET/HEAD, treats the second argument as query params. - JSON-encodes non-
BodyInitbodies and setscontent-type: application/json.
HTTP-style proxy request:
const res = await kubernetes.invoke("list-pods", {
namespace: "default",
labelSelector: "app=web",
}, {
method: "GET",
proxy: true,
});
// GET /api/plugins/kubernetes/proxy/list-pods?config_id=config-123&namespace=default&labelSelector=app%3DwebOpens an SSE stream to a plugin operation via Mission Control's /proxy/ endpoint.
const logs = pluginClient.New("kubernetes-logs", "config-123");
const events = logs.stream("tail-logs", {
pod: "api-123",
tail: 100,
});
events.onmessage = event => {
console.log(event.data);
};Browser calls the host backend. The host backend injects service auth and proxies to Mission Control.
const pluginClient = createMissionControlPluginClient({
mode: "proxy",
baseUrl: "/api/mission-control",
});Browser calls Mission Control directly using Mission Control cookies/session.
const pluginClient = createMissionControlPluginClient({
mode: "pass-through",
baseUrl: "https://mission-control.example.com",
});Pass-through requires Mission Control cookies and CORS to support credentialed browser requests.
Important exported types:
type ConnectionMode = "pass-through" | "proxy";
type QueryValue = string | number | boolean | null | undefined;
type QueryParams = Record<string, QueryValue | readonly QueryValue[]>;
interface MissionControlPluginClient {
mode: ConnectionMode;
baseUrl: string;
New(pluginRef: string, configId?: string): MissionControlPluginInstance;
}
interface MissionControlPluginInstance {
pluginRef: string;
configId?: string;
invoke(operation: string, bodyOrQueryParams?: unknown, options?: PluginInvokeOptions): Promise<Response>;
stream(operation: string, query?: QueryParams): EventSource;
}Build plugin UIs as relocatable static apps:
- Use relative asset URLs. For Vite, set
base: "./". - Use hash routing for internal UI routes.
- Use
instance.invoke()andinstance.stream()for plugin backend calls instead of hardcoding/api/plugins/...URLs.
Vite example:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
base: "./",
});pnpm install
pnpm test
pnpm build