@termix-ssh/plugin-sdk 1.0.1 → 1.0.4

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 (40) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/LICENSE +202 -0
  3. package/README.md +4 -3
  4. package/cli/commands/build.mjs +37 -12
  5. package/cli/commands/dev.mjs +1 -1
  6. package/cli/commands/openapi.mjs +39 -0
  7. package/cli/commands/validate.mjs +67 -0
  8. package/cli/index.mjs +3 -0
  9. package/cli/lib/css-classes.mjs +37 -0
  10. package/dist/changelog.d.ts +3 -8
  11. package/dist/changelog.js +6 -14
  12. package/dist/docs.d.ts +28 -0
  13. package/dist/docs.js +39 -0
  14. package/dist/frontend.d.ts +17 -0
  15. package/dist/frontend.js +8 -0
  16. package/dist/host/{chunk-COGRSHKO.js → chunk-F5RZH6HE.js} +13 -2
  17. package/dist/host/{chunk-3KG44G55.js → chunk-O2OHNVFX.js} +1 -1
  18. package/dist/host/{chunk-7GL25GFS.js → chunk-RU4E5XLY.js} +531 -300
  19. package/dist/host/core-classes.json +1 -0
  20. package/dist/host/{linked-server-BD573BEG.js → linked-server-4VWYYABP.js} +1 -1
  21. package/dist/host/{rbac-api-7GHDJLU4.js → rbac-api-WIJVZH3M.js} +1 -1
  22. package/dist/host/testing-host.js +3 -3
  23. package/dist/host/types/src/ui/api/plugins-api.d.ts +23 -0
  24. package/dist/host/types/src/ui/api/system-status-api.d.ts +24 -0
  25. package/dist/host/types/src/ui/components/docs-link.d.ts +25 -0
  26. package/dist/host/types/src/ui/components/panel-layout.d.ts +3 -1
  27. package/dist/host/types/src/ui/hooks/use-installed-plugins.d.ts +6 -0
  28. package/dist/host/types/src/ui/lib/docs.d.ts +10 -0
  29. package/dist/host/types/src/ui/main-axios.d.ts +1 -0
  30. package/dist/host/types/src/ui/plugin-host/sdk-ui.d.ts +1 -0
  31. package/dist/host/types/src/ui/settings/settings-fields-util.d.ts +10 -0
  32. package/dist/host/ui.js +139 -110
  33. package/dist/host/{use-permissions-SKWXHWDJ.js → use-permissions-KIQCJ35M.js} +1 -1
  34. package/dist/manifest.d.ts +16 -1
  35. package/dist/manifest.js +84 -1
  36. package/dist/openapi.d.ts +43 -0
  37. package/dist/openapi.js +60 -0
  38. package/dist/testing/setup.js +1 -0
  39. package/package.json +12 -2
  40. package/schema/manifest.schema.json +27 -0
package/dist/changelog.js CHANGED
@@ -3,12 +3,7 @@
3
3
  *
4
4
  * # Changelog
5
5
  *
6
- * ## Unreleased
7
- *
8
- * ### Added
9
- * - Something merged but not shipped yet
10
- *
11
- * ## 1.1.0 - 2026-10-06
6
+ * ## 1.1.0
12
7
  *
13
8
  * An optional summary paragraph.
14
9
  *
@@ -19,7 +14,8 @@
19
14
  * - Something broken
20
15
  *
21
16
  * Releases are listed newest first, and the newest one has to be the
22
- * manifest version. The date is optional until the version ships.
17
+ * manifest version. Notes go straight under the version they ship in, there
18
+ * is no Unreleased section.
23
19
  */
24
20
  import semver from "semver";
25
21
  export const CHANGE_TYPES = [
@@ -94,7 +90,7 @@ export function parseReleaseNotes(body, where = "release") {
94
90
  /** Parses a whole CHANGELOG.md. Problems are reported, never thrown. */
95
91
  export function parseChangelog(markdown) {
96
92
  const problems = [];
97
- const changelog = { unreleased: [], releases: [] };
93
+ const changelog = { releases: [] };
98
94
  const lines = markdown.replace(/\r/g, "").split("\n");
99
95
  const blocks = [];
100
96
  let fence = false;
@@ -109,13 +105,9 @@ export function parseChangelog(markdown) {
109
105
  }
110
106
  });
111
107
  const seen = new Set();
112
- blocks.forEach((block, i) => {
108
+ blocks.forEach((block) => {
113
109
  if (UNRELEASED_HEADING.test(block.heading)) {
114
- if (i !== 0)
115
- problems.push(`"## Unreleased" has to come first`);
116
- const notes = parseReleaseNotes(block.body.join("\n"), "Unreleased");
117
- problems.push(...notes.problems);
118
- changelog.unreleased = notes.changes;
110
+ problems.push(`line ${block.line}: drop "## Unreleased", notes go under the version they ship in`);
119
111
  return;
120
112
  }
121
113
  const match = RELEASE_HEADING.exec(block.heading);
package/dist/docs.d.ts ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Docs links and env var entries from a manifest. Kept apart from manifest.ts
3
+ * so the browser can use them without pulling in semver.
4
+ */
5
+ /**
6
+ * A docs link, or null for anything that is not an https URL. Core only ever
7
+ * opens it in a new tab, but a javascript: or file: link should never get there.
8
+ */
9
+ export declare function pluginDocsUrl(value: unknown): string | null;
10
+ /** Joins a page and anchor onto a plugin's docs URL. */
11
+ export declare function pluginDocsPage(base: string, page?: string, anchor?: string): string;
12
+ export declare const ENV_NAME_PATTERN: RegExp;
13
+ export declare const MAX_ENV_DESCRIPTION_LENGTH = 300;
14
+ /**
15
+ * An environment variable a plugin reads. Only shown in docs and on the
16
+ * plugin's page, so people know what they can set.
17
+ */
18
+ export interface PluginEnvVar {
19
+ name: string;
20
+ description: string;
21
+ /** The value used when it is not set, as written in a compose file. */
22
+ default?: string;
23
+ required?: boolean;
24
+ /** Holds a password or key. */
25
+ secret?: boolean;
26
+ }
27
+ /** The usable env entries of a manifest. Bad ones are dropped. */
28
+ export declare function pluginEnvVars(value: unknown): PluginEnvVar[];
package/dist/docs.js ADDED
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Docs links and env var entries from a manifest. Kept apart from manifest.ts
3
+ * so the browser can use them without pulling in semver.
4
+ */
5
+ /**
6
+ * A docs link, or null for anything that is not an https URL. Core only ever
7
+ * opens it in a new tab, but a javascript: or file: link should never get there.
8
+ */
9
+ export function pluginDocsUrl(value) {
10
+ if (typeof value !== "string")
11
+ return null;
12
+ try {
13
+ const url = new URL(value.trim());
14
+ if (url.protocol !== "https:")
15
+ return null;
16
+ return url.toString().replace(/\/$/, "");
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ /** Joins a page and anchor onto a plugin's docs URL. */
23
+ export function pluginDocsPage(base, page, anchor) {
24
+ const clean = (page ?? "").replace(/^\/+|\/+$/g, "");
25
+ const url = clean ? `${base}/${clean}` : base;
26
+ return anchor ? `${url}#${anchor.replace(/^#/, "")}` : url;
27
+ }
28
+ export const ENV_NAME_PATTERN = /^[A-Z][A-Z0-9_]{0,63}$/;
29
+ export const MAX_ENV_DESCRIPTION_LENGTH = 300;
30
+ /** The usable env entries of a manifest. Bad ones are dropped. */
31
+ export function pluginEnvVars(value) {
32
+ if (!Array.isArray(value))
33
+ return [];
34
+ return value.filter((v) => typeof v === "object" &&
35
+ v !== null &&
36
+ typeof v.name === "string" &&
37
+ ENV_NAME_PATTERN.test(v.name) &&
38
+ typeof v.description === "string");
39
+ }
@@ -816,6 +816,15 @@ export interface DesktopApi {
816
816
  /** Called when the desktop app connects to or leaves a remote server. */
817
817
  onRemoteServerChange: (listener: () => void) => Disposer;
818
818
  }
819
+ /** Links into this plugin's docs, from the manifest's docs field. */
820
+ export interface DocsApi {
821
+ /** The docs link, or null when the manifest has none. */
822
+ readonly url: string | null;
823
+ /** A page of the docs ("setup"), with an optional anchor. */
824
+ page: (page?: string, anchor?: string) => string | null;
825
+ /** Opens a page in a new tab. Does nothing without a docs link. */
826
+ open: (page?: string, anchor?: string) => void;
827
+ }
819
828
  export interface TermixAppInfo {
820
829
  readonly pluginId: string;
821
830
  readonly manifest: PluginManifest;
@@ -913,6 +922,8 @@ export interface TermixApp extends TermixAppInfo {
913
922
  confirm: (options: ConfirmRequest) => Promise<boolean>;
914
923
  /** The desktop app, when the frontend runs in it. */
915
924
  desktop: DesktopApi;
925
+ /** This plugin's docs. Needs plugin API 1.2. */
926
+ docs: DocsApi;
916
927
  /**
917
928
  * Fires after this plugin's settings are saved from the settings screen or
918
929
  * the host editor. Disposed automatically.
@@ -966,6 +977,7 @@ export interface SshAuthTypeInfo {
966
977
  */
967
978
  export interface PluginHostBridge {
968
979
  usePluginId: () => string;
980
+ useDocsUrl: (pluginId: string, page?: string, anchor?: string) => string | null;
969
981
  useTranslation: (pluginId: string) => {
970
982
  t: TranslateFn;
971
983
  language: string;
@@ -1107,6 +1119,11 @@ export interface HostStatusInfo {
1107
1119
  export declare function __setPluginHost(bridge: PluginHostBridge | null): void;
1108
1120
  /** The id of the plugin that registered the component being rendered. */
1109
1121
  export declare function usePluginId(): string;
1122
+ /**
1123
+ * A page of this plugin's docs, or null when the manifest has no docs link.
1124
+ * Pass it to PanelShell's docs prop or use DocsLink. Needs plugin API 1.2.
1125
+ */
1126
+ export declare function useDocsUrl(page?: string, anchor?: string): string | null;
1110
1127
  /**
1111
1128
  * Translation bound to this plugin's namespace. Keys the plugin does not
1112
1129
  * define fall back to core's shared strings (common.*, hosts.*).
package/dist/frontend.js CHANGED
@@ -49,6 +49,14 @@ function requireHost() {
49
49
  export function usePluginId() {
50
50
  return requireHost().usePluginId();
51
51
  }
52
+ /**
53
+ * A page of this plugin's docs, or null when the manifest has no docs link.
54
+ * Pass it to PanelShell's docs prop or use DocsLink. Needs plugin API 1.2.
55
+ */
56
+ export function useDocsUrl(page, anchor) {
57
+ const bridge = requireHost();
58
+ return bridge.useDocsUrl(bridge.usePluginId(), page, anchor);
59
+ }
52
60
  /**
53
61
  * Translation bound to this plugin's namespace. Keys the plugin does not
54
62
  * define fall back to core's shared strings (common.*, hosts.*).
@@ -5037,7 +5037,7 @@ async function updateRole(roleId, roleData) {
5037
5037
  try {
5038
5038
  const response = await rbacApi.put(`/rbac/roles/${roleId}`, roleData);
5039
5039
  if (roleData.permissions) {
5040
- const { notifyPermissionsChanged } = await import("./use-permissions-SKWXHWDJ.js");
5040
+ const { notifyPermissionsChanged } = await import("./use-permissions-KIQCJ35M.js");
5041
5041
  notifyPermissionsChanged();
5042
5042
  }
5043
5043
  return response.data;
@@ -5266,6 +5266,7 @@ async function getUserPreferences() {
5266
5266
  }
5267
5267
 
5268
5268
  // src/ui/main-axios.ts
5269
+ var TRUST_TOKEN_KEY = "termixTrustToken";
5269
5270
  function getLoggerForService(serviceName) {
5270
5271
  if (serviceName.includes("SSH") || serviceName.includes("ssh")) {
5271
5272
  return sshLogger;
@@ -5427,6 +5428,15 @@ function createApiInstance(baseURL, serviceName = "API") {
5427
5428
  config.headers["Authorization"] = `Bearer ${jwt}`;
5428
5429
  }
5429
5430
  }
5431
+ const trustToken = localStorage.getItem(TRUST_TOKEN_KEY);
5432
+ const isLogin = config.url?.includes("/users/login") || /\/users\/auth\/[^/]+\/verify$/.test(config.url ?? "");
5433
+ if (trustToken && isLogin) {
5434
+ if (config.headers.set) {
5435
+ config.headers.set("X-Termix-Trust-Token", trustToken);
5436
+ } else {
5437
+ config.headers["X-Termix-Trust-Token"] = trustToken;
5438
+ }
5439
+ }
5430
5440
  }
5431
5441
  if (typeof window !== "undefined" && window.ReactNativeWebView) {
5432
5442
  let platform = "Unknown";
@@ -5587,7 +5597,7 @@ function createRemoteOriginApiInstance(path) {
5587
5597
  });
5588
5598
  instance.interceptors.request.use(
5589
5599
  async (config) => {
5590
- const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-BD573BEG.js");
5600
+ const { getLinkedSession: getLinkedSession2 } = await import("./linked-server-4VWYYABP.js");
5591
5601
  const linked = await getLinkedSession2();
5592
5602
  config.baseURL = linked ? `${linked.serverUrl}${path}` : "http://no-server-configured";
5593
5603
  if (config.headers.set) {
@@ -6009,6 +6019,7 @@ export {
6009
6019
  patchOpenTab,
6010
6020
  parseCustomKeybindings,
6011
6021
  getUserPreferences,
6022
+ TRUST_TOKEN_KEY,
6012
6023
  setCookie,
6013
6024
  getCookie,
6014
6025
  markUserAuthenticated,
@@ -21,7 +21,7 @@ var inFlight = null;
21
21
  function load() {
22
22
  if (cache) return Promise.resolve(cache);
23
23
  if (inFlight) return inFlight;
24
- inFlight = import("./rbac-api-7GHDJLU4.js").then(({ getMyPermissions }) => getMyPermissions()).then((result) => {
24
+ inFlight = import("./rbac-api-WIJVZH3M.js").then(({ getMyPermissions }) => getMyPermissions()).then((result) => {
25
25
  cache = {
26
26
  permissions: result.permissions ?? [],
27
27
  isAdmin: !!result.isAdmin