chore: lock the plugin boundary and remove 2.9.0 leftovers

This commit is contained in:
LukeGus committed 2026-09-25 07:24:46 -05:00
1 parent 6489b62ece
commit 2961297989
399 files changed
+3964 -5627

No files matched your search

+1
View File
@@ -118,6 +118,7 @@ export function createPluginApp(
labelKey: key(item.titleKey),
kind: item.kind === "tab" ? "tab" : undefined,
hideable: item.hideable,
simplePreset: item.simplePreset,
promotable: item.promotable,
rightDockable: item.rightDockable,
mobilePrimary: item.mobilePrimary,
-7
View File
@@ -50,7 +50,6 @@ const loginMethods = createRegistry<LoginMethodDef>();
const secondFactors = createRegistry<SecondFactorDef>();
export const registerSshAuthEditor = sshAuthEditors.register;
export const getSshAuthEditor = sshAuthEditors.get;
export const useSshAuthEditors = sshAuthEditors.useList;
export const registerLoginMethod = loginMethods.register;
@@ -62,9 +61,3 @@ export const registerSecondFactor = secondFactors.register;
export const getSecondFactorUI = secondFactors.get;
export const useSecondFactors = secondFactors.useList;
export const listSecondFactorUIs = secondFactors.list;
export function resetAuthRegistries(): void {
sshAuthEditors.reset();
loginMethods.reset();
secondFactors.reset();
}
+19 -8
View File
@@ -30,7 +30,8 @@ import {
} from "@/api/plugins-api";
import { getCookie, getUserInfo } from "@/main-axios";
import { logActivity } from "@/api/dashboard-api";
import { getHostPassword } from "@/api/credentials-api";
import { getCredentials, getHostPassword } from "@/api/credentials-api";
import { getSSHHosts } from "@/api/ssh-host-management-api";
import {
getUserPreferences,
parseCustomKeybindings,
@@ -51,8 +52,8 @@ import { useOptionalHostStatusEntry } from "@/lib/ServerStatusContext";
/**
* Core permission groups, mirroring RESERVED_PERMISSION_PREFIXES in the SDK
* manifest module, which is not imported here because it pulls semver into
* the browser bundle. A group that is itself a plugin id (snippets, once)
* does not need an entry here: knownPluginIds() below already covers it.
* the browser bundle. A plugin's own namespace is covered by knownPluginIds()
* below.
*/
const CORE_PERMISSION_GROUPS = ["hosts", "credentials", "admin"];
@@ -119,11 +120,6 @@ function loadCurrentUser() {
return currentUser;
}
/** Forgets the cached user, on logout. */
export function resetBridgeUser(): void {
currentUser = null;
}
const SETTINGS_READERS: Record<
SettingsScope,
(pluginId: string, hostId?: number) => Promise<Record<string, unknown>>
@@ -295,6 +291,7 @@ export const pluginHostBridge: PluginHostBridge = {
pluginId: option.pluginId,
credentialType: option.credentialType,
supportsBackground: option.supportsBackground,
quickConnect: option.quickConnect === true,
})),
};
},
@@ -367,6 +364,20 @@ export const pluginHostBridge: PluginHostBridge = {
parseCustomKeybindings((await getUserPreferences()).customKeybindings),
setHostAutoTmux: (hostId, autoTmux) => setHostAutoTmux(hostId, autoTmux),
getClientPreference: (name) => getCookie(name),
listHosts: async () =>
(await getSSHHosts({ includeStatus: false })).map(toHostRecord),
listCredentials: async () => {
const raw = await getCredentials();
const list = Array.isArray(raw)
? raw
: ((raw as { credentials?: unknown[] }).credentials ?? []);
return (list as Record<string, unknown>[]).map((c) => ({
id: Number(c.id),
name: String(c.name ?? ""),
username: typeof c.username === "string" ? c.username : undefined,
authType: typeof c.authType === "string" ? c.authType : undefined,
}));
},
},
};
-10
View File
@@ -40,10 +40,6 @@ export function registerPluginComponent(
};
}
export function getPluginComponent(id: string): AnyComponent | undefined {
return components.get(id);
}
function subscribe(listener: () => void): () => void {
listeners.add(listener);
return () => listeners.delete(listener);
@@ -72,9 +68,3 @@ export function PluginComponent({
if (!Component) return fallback as ReactNode;
return createElement(Component, props);
}
/** Test seam. */
export function resetPluginComponents(): void {
components.clear();
notify();
}
@@ -26,8 +26,6 @@ export interface RegisteredHomepageWidget {
const registry = createRegistry<RegisteredHomepageWidget>();
export const registerHomepageWidgetType = registry.register;
export const unregisterHomepageWidgetType = registry.unregister;
export const getHomepageWidgetType = registry.get;
export const listHomepageWidgetTypes = registry.list;
export const useHomepageWidgetTypes = registry.useList;
export const resetHomepageWidgetTypes = registry.reset;
+1 -1
View File
@@ -327,7 +327,7 @@ export function syncPlugins(summaries?: PluginSummary[]): Promise<void> {
}
/** Fired by anything that changes plugin state, e.g. the admin toggle. */
export const PLUGINS_CHANGED_EVENT = "termix:plugins-changed";
const PLUGINS_CHANGED_EVENT = "termix:plugins-changed";
let lastFocusSync = 0;
const FOCUS_SYNC_INTERVAL_MS = 30_000;
+1 -1
View File
@@ -38,7 +38,7 @@ function commit(next: StoreState): void {
for (const listener of listeners) listener();
}
export function subscribePluginStore(listener: () => void): () => void {
function subscribePluginStore(listener: () => void): () => void {
listeners.add(listener);
return () => {
listeners.delete(listener);
+1 -1
View File
@@ -75,7 +75,7 @@ class PluginErrorBoundary extends Component<BoundaryProps, { error: boolean }> {
}
}
export function PluginScope({
function PluginScope({
pluginId,
children,
}: {
+37
View File
@@ -159,3 +159,40 @@ export {
testNotificationChannel,
type NotificationChannel,
} from "@/api/notification-channels-api";
// Keyboard hints, the connection log panel and a named console logger.
export { Kbd, KbdKey, KbdSeparator } from "@/components/kbd";
export { ConnectionLogPanel } from "@/components/connection/ConnectionLogPanel";
export type { LogEntry } from "@/types/connection-log";
export { createFrontendLogger, type LogContext } from "@/lib/frontend-logger";
// One budget for background work across the whole app (preloads, prefetches).
export {
getAdaptiveResourceBudget,
markAdaptiveResourceUsed,
runAdaptiveBackgroundTask,
} from "@/lib/adaptive-resource-budget";
// The id this browser sends as X-Termix-Device-ID.
export { getDeviceId } from "@/lib/device-id";
// A host that is never saved, for connecting to an address straight away.
export {
createQuickConnectHost,
isQuickConnectHost,
} from "@/sidebar/quick-connect-host";
// The desktop app's local file bridge (window.electronAPI.localFs and
// localTransfer), for plugins that browse or transfer local files.
export type {
LocalCollectedFile,
LocalDirectoryListing,
LocalFileEntry,
LocalFsHomeInfo,
LocalFsResult,
LocalTransferOrigin,
LocalTransferProgress,
LocalTrashResult,
LocalWalkFile,
LocalWalkResult,
} from "@/types/electron";
-4
View File
@@ -59,10 +59,6 @@ export function useShellHosts(): { hosts: Host[]; loaded: boolean } {
);
}
export function getShellHosts(): Host[] {
return hosts;
}
export function notifyTabsChanged(): void {
for (const listener of changeListeners) listener();
}
+9 -2
View File
@@ -9,7 +9,10 @@
import type { ReactElement } from "react";
import { render } from "@testing-library/react";
import type { PluginManifest } from "@termix/plugin-sdk/frontend";
import type {
HostActionContribution,
PluginManifest,
} from "@termix/plugin-sdk/frontend";
import type {
FrontendPluginModule,
RenderWithAppOptions,
@@ -27,7 +30,7 @@ import {
} from "@/shell/tab-registry";
import { getPanel, listPanels } from "@/shell/panel-registry";
import { listRegisteredRailItems } from "@/sidebar/rail-items";
import { listHostActions } from "@/sidebar/host-contributions";
import { hostActionsFor, listHostActions } from "@/sidebar/host-contributions";
import { listHostProtocols } from "@/sidebar/host-protocols";
import {
getHostEditorSection,
@@ -214,6 +217,10 @@ export async function renderPlugin(
id: action.id,
tabType: action.tabType,
})),
hostActionsFor: (host) =>
mine(
hostActionsFor(listHostActions(), host as Host),
) as unknown as HostActionContribution[],
hostProtocols: () =>
mine(listHostProtocols()).map((protocol) => protocol.id),
hostEditorSections: () =>