Files
Termix/src/backend/plugins/host-modules.ts
T
62ac931468 v26.10.1 (#1599)
* feat: AI translations at release, 7 new languages and regional fallback

* fix: proper plural forms for count strings and full translations

* fix: plural forms for selected, hidden and active counts

* fix: synced plugin updates failing on desktop and server-only plugins shown as stopped

* fix: restore rounded corners on app icons and favicon

* feat: desktop app auto updates with automatic, ask first or never modes

* feat: community and custom plugin registries

* feat: plugin guide for AI tools in the SDK with a lint check

* fix: keep the terminal WebSocket open past nginx's 60s default (#1588)

* chore: update changelog

* feat: show plugin download counts only and stop CI builds inflating them

* feat: hide installed plugins in the browse tab by default

* chore: pin bundled plugins to latest releases

* chore: update translations

* fix: focusing a saved password field and saving no longer erases it

* fix: tell desktop users when their 2.8 data needs 2.9 first

* fix: point windows update info at the x64 installer

* fix: keep desktop client tunnel sockets alive behind proxies

* chore: pin bundled plugins to latest releases

* fix: cover client tunnel keepalive in the c2s runtime tests

* fix: typing after opening a host no longer goes to the host search

* fix: accept tab and = separators in SSH config import (#1598)

* chore: update changelog

* chore: drop plugin versions from core changelog

* chore: update translations

* chore: lint, format, and bump version to 26.10.1

---------

Co-authored-by: Darnel Hunter <68697450+innotelinc@users.noreply.github.com>
Co-authored-by: Iain <Iain1970@protonmail.com>
2026-10-12 11:35:48 -05:00

66 lines
2.1 KiB
TypeScript

import module from "node:module";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { getPluginsDir } from "./paths.js";
/**
* A plugin build leaves host packages (drizzle-orm, express, ssh2, ...) for
* core to supply, and Node finds them by walking up from the importing file.
* That works for bundled plugins inside the app, but a .tmxplug unpacked into
* a data dir outside it (the desktop app keeps it in AppData) finds nothing.
* Those imports fall back to core's own copy, the one bundled plugins get.
*/
let installed = false;
export function isBareSpecifier(specifier: string): boolean {
if (specifier.startsWith(".") || specifier.startsWith("/")) return false;
if (specifier.startsWith("#")) return false;
// file:, node:, data:, and a Windows drive letter
return !/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(specifier);
}
/** Whether an import from parentURL may fall back to core's packages. */
export function isUserPluginFile(
parentURL: string | undefined,
pluginsDir: string,
): boolean {
if (!parentURL?.startsWith("file:")) return false;
let file: string;
try {
file = fileURLToPath(new URL(parentURL));
} catch {
return false;
}
const relative = path.relative(path.resolve(pluginsDir), file);
return (
relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative)
);
}
export function installHostModuleFallback(): void {
if (installed) return;
installed = true;
// Node 22.15+. Older runtimes keep plain resolution.
if (typeof module.registerHooks !== "function") return;
module.registerHooks({
resolve(specifier, context, nextResolve) {
try {
return nextResolve(specifier, context);
} catch (error) {
if (
(error as { code?: string })?.code !== "ERR_MODULE_NOT_FOUND" ||
!isBareSpecifier(specifier) ||
!isUserPluginFile(context.parentURL, getPluginsDir())
) {
throw error;
}
return nextResolve(specifier, {
...context,
parentURL: import.meta.url,
});
}
},
});
}