Files
OmniRoute/bin/cli/runtime/trayRuntime.ts
diegosouzapw 77a4429bf4 feat(cli): add standalone system tray with PowerShell fallback on Windows
Cross-platform system tray for `omniroute --tray`: Windows uses a
PowerShell NotifyIcon script (zero native binary, AV-safe); macOS/Linux
use systray2 lazy-installed at runtime into ~/.omniroute/runtime/.
Includes per-platform autostart (LaunchAgent / .desktop / registry) and
`omniroute config tray <enable|disable>` CLI command.

DISPLAY added to env-sync allowlist as an OS/X11 variable, not an
OmniRoute config variable.
2026-05-14 22:42:01 -03:00

49 lines
1.7 KiB
TypeScript

import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { homedir } from "node:os";
import { execSync } from "node:child_process";
const RUNTIME_DIR = join(homedir(), ".omniroute", "runtime");
// systray2 is a maintained fork with prebuilt binaries — installed lazily at runtime,
// not in dependencies, to avoid npm install overhead for users who don't use --tray.
const SYSTRAY_VERSION = "systray2@1.4.5";
export async function loadSystray(): Promise<(new (...args: unknown[]) => unknown) | null> {
if (process.platform === "win32") return null; // Windows uses tray.ps1 instead
ensureRuntimeDir();
if (!isInstalled()) {
try {
installSystray();
} catch (err) {
console.warn(`[omniroute] tray runtime install failed: ${(err as Error).message}`);
return null;
}
}
try {
const modPath = join(RUNTIME_DIR, "node_modules", "systray2");
const mod = await import(modPath);
return (mod.default ?? mod.SysTray ?? mod) as (new (...args: unknown[]) => unknown) | null;
} catch {
return null;
}
}
function ensureRuntimeDir(): void {
if (!existsSync(RUNTIME_DIR)) mkdirSync(RUNTIME_DIR, { recursive: true });
const pkg = join(RUNTIME_DIR, "package.json");
if (!existsSync(pkg)) {
writeFileSync(pkg, JSON.stringify({ name: "omniroute-runtime", private: true }), "utf-8");
}
}
function isInstalled(): boolean {
return existsSync(join(RUNTIME_DIR, "node_modules", "systray2", "package.json"));
}
function installSystray(): void {
execSync(
`npm install --prefix "${RUNTIME_DIR}" ${SYSTRAY_VERSION} --no-audit --no-fund --silent`,
{ stdio: ["ignore", "ignore", "pipe"], timeout: 120_000 }
);
}