mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-27 21:55:24 +00:00
* 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>
66 lines
2.1 KiB
TypeScript
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,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
}
|