@termix-ssh/plugin-sdk 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/PLUGIN-GUIDE.md +69 -1
  3. package/dist/backend.d.ts +107 -0
  4. package/dist/capabilities.js +3 -0
  5. package/dist/frontend.d.ts +19 -0
  6. package/dist/host/{PluginsTab-CM2B33BD.js → PluginsTab-MNQU5OJ3.js} +4 -4
  7. package/dist/host/{chunk-HQ6NNFSX.js → chunk-5W6F6GJJ.js} +68 -3
  8. package/dist/host/{chunk-P26QKKU5.js → chunk-6WLECSNB.js} +35 -5
  9. package/dist/host/{chunk-ROB2SH5Q.js → chunk-C52NRTA6.js} +9 -5
  10. package/dist/host/{chunk-VKNGNEF3.js → chunk-DN6YKMFJ.js} +1 -1
  11. package/dist/host/{chunk-TK3Y437H.js → chunk-EKXGDM5P.js} +1 -1
  12. package/dist/host/{chunk-JRV2HIDK.js → chunk-GRH7GSKG.js} +1 -1
  13. package/dist/host/{chunk-7J4QNGFM.js → chunk-IMPLAAUM.js} +1 -1
  14. package/dist/host/{chunk-I5F2PSMR.js → chunk-RWFXGG4Q.js} +9 -9
  15. package/dist/host/core-classes.json +1 -1
  16. package/dist/host/frame-host.css +1 -1
  17. package/dist/host/frame-host.js +5 -5
  18. package/dist/host/{linked-server-J5G457OA.js → linked-server-YCAJ2NX3.js} +1 -1
  19. package/dist/host/{rbac-api-PY22W5UY.js → rbac-api-A64TXDL5.js} +1 -1
  20. package/dist/host/testing-host.js +10 -6
  21. package/dist/host/types/src/ui/lib/db-health-monitor.d.ts +5 -0
  22. package/dist/host/types/src/ui/lib/plugin-transport.d.ts +5 -0
  23. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +4 -0
  24. package/dist/host/types/src/ui/shell/HostTabEmbed.d.ts +15 -0
  25. package/dist/host/types/src/ui/shell/host-sections.d.ts +25 -0
  26. package/dist/host/ui.js +51 -9
  27. package/dist/host/{use-permissions-EWRI3WLT.js → use-permissions-FLQAN6I7.js} +1 -1
  28. package/dist/manifest.d.ts +6 -1
  29. package/dist/manifest.js +7 -1
  30. package/dist/operations.d.ts +222 -0
  31. package/dist/operations.js +650 -0
  32. package/dist/testing.d.ts +9 -1
  33. package/dist/testing.js +45 -0
  34. package/package.json +5 -1
  35. package/schema/manifest.schema.json +25 -0
@@ -1,7 +1,7 @@
1
1
  const __termixNoGlob = () => ({});
2
2
  import {
3
3
  Toaster
4
- } from "./chunk-TK3Y437H.js";
4
+ } from "./chunk-EKXGDM5P.js";
5
5
  import {
6
6
  ThemeProvider,
7
7
  createPluginApp,
@@ -12,8 +12,8 @@ import {
12
12
  setPluginThemes,
13
13
  sshHostToHost,
14
14
  useTheme
15
- } from "./chunk-HQ6NNFSX.js";
16
- import "./chunk-JRV2HIDK.js";
15
+ } from "./chunk-5W6F6GJJ.js";
16
+ import "./chunk-GRH7GSKG.js";
17
17
  import {
18
18
  SurfaceScope,
19
19
  notifyShellReady,
@@ -21,13 +21,13 @@ import {
21
21
  setShellCallbacks,
22
22
  setShellHosts,
23
23
  shellHost
24
- } from "./chunk-VKNGNEF3.js";
24
+ } from "./chunk-DN6YKMFJ.js";
25
25
  import {
26
26
  getSSHHosts,
27
27
  getTabType,
28
28
  setFrontendState,
29
29
  setPluginSummaries
30
- } from "./chunk-P26QKKU5.js";
30
+ } from "./chunk-6WLECSNB.js";
31
31
  import {
32
32
  init_define_import_meta_env
33
33
  } from "./chunk-YGYBM7SQ.js";
@@ -4,7 +4,7 @@ import {
4
4
  getLinkedSession,
5
5
  notifySyncChanged,
6
6
  useDesktopLinked
7
- } from "./chunk-P26QKKU5.js";
7
+ } from "./chunk-6WLECSNB.js";
8
8
  import "./chunk-YGYBM7SQ.js";
9
9
  export {
10
10
  SYNC_CHANGED_EVENT,
@@ -25,7 +25,7 @@ import {
25
25
  shareHost,
26
26
  updateHostAccess,
27
27
  updateRole
28
- } from "./chunk-P26QKKU5.js";
28
+ } from "./chunk-6WLECSNB.js";
29
29
  import "./chunk-YGYBM7SQ.js";
30
30
  export {
31
31
  assignRoleToUser,
@@ -7,7 +7,7 @@ import {
7
7
  PluginViewPlaceholder,
8
8
  renderTabContent,
9
9
  trackTabHandle
10
- } from "./chunk-I5F2PSMR.js";
10
+ } from "./chunk-RWFXGG4Q.js";
11
11
  import {
12
12
  RAIL_ITEMS,
13
13
  ServerStatusProvider,
@@ -46,6 +46,7 @@ import {
46
46
  registerDashboardCard,
47
47
  registerHostAction,
48
48
  registerHostProtocol,
49
+ registerHostSection,
49
50
  registerKeybindingAction,
50
51
  registerKeybindingDefault,
51
52
  registerRailItem,
@@ -54,6 +55,7 @@ import {
54
55
  resetActionRegistry,
55
56
  resetDashboardCards,
56
57
  resetHostContributions,
58
+ resetHostSections,
57
59
  resetKeybindingRegistry,
58
60
  resetRegisteredRailItems,
59
61
  resolveLayoutTabTarget,
@@ -61,11 +63,11 @@ import {
61
63
  setRemoteServerUrlForTesting,
62
64
  useShellController,
63
65
  useTabStageStore
64
- } from "./chunk-HQ6NNFSX.js";
66
+ } from "./chunk-5W6F6GJJ.js";
65
67
  import {
66
68
  setPermissionsForTesting
67
- } from "./chunk-JRV2HIDK.js";
68
- import "./chunk-7J4QNGFM.js";
69
+ } from "./chunk-GRH7GSKG.js";
70
+ import "./chunk-IMPLAAUM.js";
69
71
  import {
70
72
  SurfaceScope,
71
73
  notifyShellReady,
@@ -73,14 +75,14 @@ import {
73
75
  setShellCallbacks,
74
76
  setShellHosts,
75
77
  setShellLayoutProvider
76
- } from "./chunk-VKNGNEF3.js";
78
+ } from "./chunk-DN6YKMFJ.js";
77
79
  import {
78
80
  getTabType,
79
81
  listTabTypes,
80
82
  resetPluginStore,
81
83
  setFrontendState,
82
84
  setPluginSummaries
83
- } from "./chunk-P26QKKU5.js";
85
+ } from "./chunk-6WLECSNB.js";
84
86
  import {
85
87
  init_define_import_meta_env
86
88
  } from "./chunk-YGYBM7SQ.js";
@@ -499,6 +501,7 @@ export {
499
501
  registerDashboardCard,
500
502
  registerHostAction,
501
503
  registerHostProtocol,
504
+ registerHostSection,
502
505
  registerKeybindingAction,
503
506
  registerKeybindingDefault,
504
507
  registerRailItem,
@@ -507,6 +510,7 @@ export {
507
510
  resetActionRegistry,
508
511
  resetDashboardCards,
509
512
  resetHostContributions,
513
+ resetHostSections,
510
514
  resetKeybindingRegistry,
511
515
  resetPluginStore,
512
516
  resetRegisteredRailItems,
@@ -19,6 +19,8 @@ declare class DatabaseHealthMonitor {
19
19
  private static instance;
20
20
  private listeners;
21
21
  private degradedActive;
22
+ private probe;
23
+ private probing;
22
24
  private constructor();
23
25
  static getInstance(): DatabaseHealthMonitor;
24
26
  on(event: string, listener: EventListener): void;
@@ -26,6 +28,9 @@ declare class DatabaseHealthMonitor {
26
28
  private emit;
27
29
  reportSessionExpired(): void;
28
30
  reportDatabaseError(error: unknown): void;
31
+ /** Sets the quick check used after a request times out. */
32
+ setReachabilityProbe(probe: (() => Promise<boolean>) | null): void;
33
+ private markDegraded;
29
34
  reportDatabaseSuccess(): void;
30
35
  isDegraded(): boolean;
31
36
  reset(): void;
@@ -25,6 +25,11 @@ import { type ConnectionOrigin, type WebSocketConnectionTarget } from "./connect
25
25
  * reporting all behave identically.
26
26
  */
27
27
  export declare function createPluginApi(pluginId: string): AxiosInstance;
28
+ /**
29
+ * An axios client rooted at core's /operations routes: the action catalog,
30
+ * runs, approvals, events, signals and cross host resources.
31
+ */
32
+ export declare function createOperationsApi(): AxiosInstance;
28
33
  /**
29
34
  * A raw fetch on this plugin's /plugin-api mount point, for a response the
30
35
  * caller reads as a stream (server-sent events). axios buffers the whole
@@ -196,3 +196,7 @@ export { useEditorDefaultKeys } from "../sidebar/host-defaults/catalog";
196
196
  export { enabledHostProtocols, type HostProtocolDef, hostProtocolFlags, type HostProtocols, listHostProtocols, protocolEnabled, protocolPort, useHostProtocols, withProtocolSettings, } from "../sidebar/host-protocols";
197
197
  export { quickConnectTargets } from "../sidebar/quick-connect-host";
198
198
  export { groupRailItems, isCoreRailView, moveRailItem, promotableIds, type RailGroup, type RailItemDef, railItemLabel, rightDockableIds, useRailItems, } from "../sidebar/rail-items";
199
+ export { hostSectionsFor, type HostSectionDef, listHostSections, useHostSections, } from "../shell/host-sections";
200
+ export { HostTabEmbed } from "../shell/HostTabEmbed";
201
+ /** Core's /operations routes, with the user's own session. */
202
+ export declare const operationsApi: import("axios").AxiosInstance;
@@ -0,0 +1,15 @@
1
+ import type { Host } from "../../types/ui-types";
2
+ import type { TabShellCallbacks } from "./tab-registry";
3
+ /**
4
+ * Draws a registered tab type inside another view, such as a host page, with
5
+ * the same props the tab stage gives it. The embed has its own instance id, so
6
+ * it never collides with the same tool opened as a normal tab.
7
+ */
8
+ export declare function HostTabEmbed({ host, type, label, data, isVisible, shell, }: {
9
+ host: Host;
10
+ type: string;
11
+ label?: string;
12
+ data?: Record<string, unknown>;
13
+ isVisible?: boolean;
14
+ shell: TabShellCallbacks;
15
+ }): import("react").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { ComponentType } from "react";
2
+ import type { Host } from "../../types/ui-types";
3
+ import type { TabShellCallbacks } from "./tab-registry";
4
+ export interface HostSectionRenderProps {
5
+ host: Host;
6
+ isVisible: boolean;
7
+ shell: TabShellCallbacks;
8
+ }
9
+ /**
10
+ * A card a plugin adds to a host page. The page itself belongs to a plugin;
11
+ * core only keeps the list so any plugin can fill it.
12
+ */
13
+ export interface HostSectionDef {
14
+ id: string;
15
+ pluginId?: string;
16
+ titleKey: string;
17
+ order?: number;
18
+ when: (host: Host) => boolean;
19
+ component: ComponentType<HostSectionRenderProps>;
20
+ }
21
+ export declare const registerHostSection: (item: HostSectionDef) => () => void;
22
+ export declare const useHostSections: () => HostSectionDef[];
23
+ export declare const listHostSections: () => HostSectionDef[];
24
+ export declare const resetHostSections: () => void;
25
+ export declare function hostSectionsFor(all: HostSectionDef[], host: Host): HostSectionDef[];
package/dist/host/ui.js CHANGED
@@ -16,15 +16,16 @@ import {
16
16
  markTabSurfaceUsed,
17
17
  preloadTabSurface,
18
18
  recordHostActionPreference,
19
+ renderTabContent,
19
20
  runAdaptiveBackgroundTask,
20
21
  tabIcon,
21
22
  useInstalledPlugins,
22
23
  usePluginHostSections,
23
24
  useTabHandlesVersion
24
- } from "./chunk-I5F2PSMR.js";
25
+ } from "./chunk-RWFXGG4Q.js";
25
26
  import {
26
27
  Toaster
27
- } from "./chunk-TK3Y437H.js";
28
+ } from "./chunk-EKXGDM5P.js";
28
29
  import {
29
30
  ACCENT_PRESET_COLORS,
30
31
  BUILTIN_THEMES,
@@ -48,6 +49,7 @@ import {
48
49
  canJoinSplit,
49
50
  challengeSecondFactor,
50
51
  changeAppLanguage,
52
+ createOperationsApi,
51
53
  createPluginApp,
52
54
  createQuickConnectHost,
53
55
  defaultConnectAction,
@@ -75,6 +77,7 @@ import {
75
77
  hostEditorToolbarItemsFor,
76
78
  hostMenuItemsFor,
77
79
  hostProtocolFlags,
80
+ hostSectionsFor,
78
81
  hostStatusIndicatorsFor,
79
82
  i18n_default,
80
83
  installPluginHostBridge,
@@ -86,6 +89,7 @@ import {
86
89
  layoutLabel,
87
90
  listHostActions,
88
91
  listHostProtocols,
92
+ listHostSections,
89
93
  listPanes,
90
94
  listRegisteredRailItems,
91
95
  loadPaletteGroup,
@@ -139,6 +143,7 @@ import {
139
143
  useHostEditorSections,
140
144
  useHostEditorToolbarItems,
141
145
  useHostProtocols,
146
+ useHostSections,
142
147
  useHostStatus,
143
148
  useHostStatusIndicators,
144
149
  useIsMobile,
@@ -165,10 +170,10 @@ import {
165
170
  useThemes,
166
171
  verifySecondFactor,
167
172
  withProtocolSettings
168
- } from "./chunk-HQ6NNFSX.js";
173
+ } from "./chunk-5W6F6GJJ.js";
169
174
  import {
170
175
  usePermissions
171
- } from "./chunk-JRV2HIDK.js";
176
+ } from "./chunk-GRH7GSKG.js";
172
177
  import {
173
178
  AddButton,
174
179
  Card,
@@ -189,7 +194,7 @@ import {
189
194
  isPrerelease,
190
195
  reportBetaFeedbackUrl,
191
196
  reportCoreIssueUrl
192
- } from "./chunk-ROB2SH5Q.js";
197
+ } from "./chunk-C52NRTA6.js";
193
198
  import {
194
199
  Button,
195
200
  DefaultsOnly,
@@ -206,7 +211,7 @@ import {
206
211
  hasVisibleFields,
207
212
  isFieldShown,
208
213
  useIsDefaultsEditor
209
- } from "./chunk-7J4QNGFM.js";
214
+ } from "./chunk-IMPLAAUM.js";
210
215
  import {
211
216
  Action,
212
217
  Activity,
@@ -381,7 +386,7 @@ import {
381
386
  useSurfaceKind,
382
387
  useUiPreference,
383
388
  useUiPreferencesContext
384
- } from "./chunk-VKNGNEF3.js";
389
+ } from "./chunk-DN6YKMFJ.js";
385
390
  import {
386
391
  CREDENTIAL_ROW_FIELD_KEYS,
387
392
  PRESETS,
@@ -544,7 +549,7 @@ import {
544
549
  useTabs,
545
550
  useTabsSafe,
546
551
  verifyPasswordResetCode
547
- } from "./chunk-P26QKKU5.js";
552
+ } from "./chunk-6WLECSNB.js";
548
553
  import {
549
554
  define_import_meta_env_default,
550
555
  init_define_import_meta_env
@@ -557,7 +562,7 @@ init_define_import_meta_env();
557
562
  init_define_import_meta_env();
558
563
  import { jsx } from "react/jsx-runtime";
559
564
  var alertVariants = cva(
560
- "relative w-full border border-border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
565
+ "relative w-full border border-border px-4 py-3 text-sm not-has-[>svg]:block grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
561
566
  {
562
567
  variants: {
563
568
  variant: {
@@ -33305,6 +33310,38 @@ function AuthEnrollmentSections({
33305
33310
  );
33306
33311
  }) });
33307
33312
  }
33313
+
33314
+ // src/ui/shell/HostTabEmbed.tsx
33315
+ init_define_import_meta_env();
33316
+ import { useMemo as useMemo10 } from "react";
33317
+ import { jsx as jsx55 } from "react/jsx-runtime";
33318
+ function HostTabEmbed({
33319
+ host,
33320
+ type,
33321
+ label,
33322
+ data,
33323
+ isVisible = true,
33324
+ shell: shell2
33325
+ }) {
33326
+ const tab = useMemo10(
33327
+ () => ({
33328
+ id: `embed-${type}-${host.id}`,
33329
+ instanceId: `embed-${type}-${host.id}`,
33330
+ type,
33331
+ label: label ?? host.name ?? host.ip,
33332
+ host,
33333
+ openedAt: Date.now(),
33334
+ data
33335
+ }),
33336
+ // A new host or tool is a new tab; label and data changes keep it.
33337
+ // eslint-disable-next-line react-hooks/exhaustive-deps
33338
+ [type, host.id]
33339
+ );
33340
+ return /* @__PURE__ */ jsx55("div", { className: "h-full min-h-0 flex flex-col", children: renderTabContent(tab, { shell: shell2, isVisible, isFocusedPane: isVisible }) });
33341
+ }
33342
+
33343
+ // src/ui/plugin-host/sdk-ui.ts
33344
+ var operationsApi = createOperationsApi();
33308
33345
  export {
33309
33346
  ACCENT_PRESET_COLORS,
33310
33347
  APP_VERSION,
@@ -33404,6 +33441,7 @@ export {
33404
33441
  HostFeatureFields,
33405
33442
  HostKeyVerificationDialog,
33406
33443
  HostOnly,
33444
+ HostTabEmbed,
33407
33445
  IconPicker,
33408
33446
  InlineView,
33409
33447
  Input,
@@ -33638,6 +33676,7 @@ export {
33638
33676
  hostEditorToolbarItemsFor,
33639
33677
  hostMenuItemsFor,
33640
33678
  hostProtocolFlags,
33679
+ hostSectionsFor,
33641
33680
  hostStatusIndicatorsFor,
33642
33681
  hydrateLocalSharedHostAuth,
33643
33682
  invalidateHostsAndStatusCaches,
@@ -33656,6 +33695,7 @@ export {
33656
33695
  remoteServerUrl as linkedServerUrl,
33657
33696
  listHostActions,
33658
33697
  listHostProtocols,
33698
+ listHostSections,
33659
33699
  loadPaletteGroup,
33660
33700
  logoutUser,
33661
33701
  makeHostSshSubTabs,
@@ -33669,6 +33709,7 @@ export {
33669
33709
  normalizeLanguageCode,
33670
33710
  notifySyncChanged,
33671
33711
  openInSplit,
33712
+ operationsApi,
33672
33713
  paletteEntriesFor,
33673
33714
  parseCustomKeybindings,
33674
33715
  parseServerTime,
@@ -33779,6 +33820,7 @@ export {
33779
33820
  useHostEditorSections,
33780
33821
  useHostEditorToolbarItems,
33781
33822
  useHostProtocols,
33823
+ useHostSections,
33782
33824
  useHostStatus,
33783
33825
  useHostStatusIndicators,
33784
33826
  useInstalledPlugins,
@@ -7,7 +7,7 @@ import {
7
7
  resetPermissionsCache,
8
8
  setPermissionsForTesting,
9
9
  usePermissions
10
- } from "./chunk-JRV2HIDK.js";
10
+ } from "./chunk-GRH7GSKG.js";
11
11
  import "./chunk-YGYBM7SQ.js";
12
12
  export {
13
13
  hasPermission,
@@ -9,6 +9,7 @@
9
9
  * "permissionGroups" used to validate clean and be silently dropped, which
10
10
  * made a manifest look like it declared something it did not.
11
11
  */
12
+ import { type PluginOperationsContribution } from "./operations.js";
12
13
  import { type PluginEnvVar } from "./docs.js";
13
14
  export { validateThemeCss } from "./theme-css.js";
14
15
  export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDocsUrl, pluginEnvVars, type PluginEnvVar, } from "./docs.js";
@@ -20,7 +21,7 @@ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDoc
20
21
  * "^1.2" needs 1.2 or later). engine.termix is checked against the core
21
22
  * version by the server.
22
23
  */
23
- export declare const PLUGIN_API_VERSION = "1.13.0";
24
+ export declare const PLUGIN_API_VERSION = "1.14.0";
24
25
  /** The API major, the default engine.api for a new plugin. */
25
26
  export declare const SUPPORTED_PLUGIN_API_VERSION: string;
26
27
  /** Whether this build's plugin API satisfies a manifest's engine.api. */
@@ -305,6 +306,10 @@ export interface PluginContributions {
305
306
  tabs?: PluginTabContribution[];
306
307
  panels?: PluginViewContribution[];
307
308
  dashboardCards?: PluginViewContribution[];
309
+ /** Cards on the host page, filled with app.registerHostSection. */
310
+ hostSections?: PluginViewContribution[];
311
+ /** What this plugin can do, for every surface to find on its own. */
312
+ operations?: PluginOperationsContribution;
308
313
  /**
309
314
  * The frontend also runs on anonymous guest pages (shared-session and
310
315
  * collab links), where it is activated with app.guest set.
package/dist/manifest.js CHANGED
@@ -11,6 +11,7 @@
11
11
  */
12
12
  import semver from "semver";
13
13
  import { isKnownCapability } from "./capabilities.js";
14
+ import { checkOperationReferences, validateOperations, } from "./operations.js";
14
15
  import { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsUrl, } from "./docs.js";
15
16
  export { validateThemeCss } from "./theme-css.js";
16
17
  export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDocsUrl, pluginEnvVars, } from "./docs.js";
@@ -22,7 +23,7 @@ export { ENV_NAME_PATTERN, MAX_ENV_DESCRIPTION_LENGTH, pluginDocsPage, pluginDoc
22
23
  * "^1.2" needs 1.2 or later). engine.termix is checked against the core
23
24
  * version by the server.
24
25
  */
25
- export const PLUGIN_API_VERSION = "1.13.0";
26
+ export const PLUGIN_API_VERSION = "1.14.0";
26
27
  /** The API major, the default engine.api for a new plugin. */
27
28
  export const SUPPORTED_PLUGIN_API_VERSION = String(semver.major(PLUGIN_API_VERSION));
28
29
  /** Whether this build's plugin API satisfies a manifest's engine.api. */
@@ -245,6 +246,8 @@ const ALLOWED_CONTRIBUTES = new Set([
245
246
  "tabs",
246
247
  "panels",
247
248
  "dashboardCards",
249
+ "hostSections",
250
+ "operations",
248
251
  "guest",
249
252
  "guestViews",
250
253
  "actions",
@@ -835,6 +838,8 @@ function validateContributes(contributes, pluginId, errors) {
835
838
  validateTabs(contributes.tabs, errors);
836
839
  validateViews(contributes.panels, "panels", errors);
837
840
  validateViews(contributes.dashboardCards, "dashboardCards", errors);
841
+ validateViews(contributes.hostSections, "hostSections", errors);
842
+ validateOperations(contributes.operations, pluginId, errors);
838
843
  if (contributes.guest !== undefined &&
839
844
  typeof contributes.guest !== "boolean") {
840
845
  errors.push('Field "contributes.guest" must be a boolean');
@@ -1767,6 +1772,7 @@ export function parseManifest(raw) {
1767
1772
  errors.push(`contributes.actions["${action.id}"].permission "${action.permission}" is not declared in contributes.permissions`);
1768
1773
  }
1769
1774
  }
1775
+ checkOperationReferences(manifest.contributes?.operations, manifest.capabilities, (name) => declared.has(qualifyPermission(manifest.id, name)), errors);
1770
1776
  // A settings field may gate on one of this plugin's own permissions, or on
1771
1777
  // a core/other-plugin id used as given. Only the first form is checkable
1772
1778
  // here, and an undeclared one would hide the field from every admin.