* Centralize subprocess env boundaries * Remove env boundary static audit test * Tighten env boundary process launches * Fix Windows native process launches * Inline provider env pass-throughs
102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { existsSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import path from "node:path";
|
|
import { spawnProcess } from "@getpaseo/server";
|
|
|
|
function findDesktopApp(): string | null {
|
|
if (process.platform === "darwin") {
|
|
const candidates = [
|
|
"/Applications/Paseo.app",
|
|
path.join(homedir(), "Applications", "Paseo.app"),
|
|
];
|
|
|
|
for (const candidate of candidates) {
|
|
if (existsSync(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
if (process.platform === "linux") {
|
|
const candidates = [
|
|
"/usr/bin/Paseo",
|
|
"/opt/Paseo/Paseo",
|
|
path.join(homedir(), "Applications", "Paseo.AppImage"),
|
|
];
|
|
|
|
for (const candidate of candidates) {
|
|
if (existsSync(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
if (process.platform === "win32") {
|
|
const localAppData = process.env.LOCALAPPDATA;
|
|
if (!localAppData) {
|
|
return null;
|
|
}
|
|
|
|
const candidate = path.join(localAppData, "Programs", "Paseo", "Paseo.exe");
|
|
return existsSync(candidate) ? candidate : null;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function cleanEnvForDesktopLaunch(): NodeJS.ProcessEnv {
|
|
const env = { ...process.env };
|
|
// The CLI runs via ELECTRON_RUN_AS_NODE=1. On Linux/Windows the spawned
|
|
// desktop process inherits the env directly, so we must strip it or the
|
|
// desktop app would start as a bare Node process instead of Electron.
|
|
delete env.ELECTRON_RUN_AS_NODE;
|
|
delete env.ELECTRON_NO_ATTACH_CONSOLE;
|
|
delete env.PASEO_NODE_ENV;
|
|
return env;
|
|
}
|
|
|
|
function spawnDetached(command: string, args: string[]): void {
|
|
spawnProcess(command, args, {
|
|
detached: true,
|
|
stdio: "ignore",
|
|
env: cleanEnvForDesktopLaunch(),
|
|
}).unref();
|
|
}
|
|
|
|
export async function openDesktopWithProject(projectPath: string): Promise<void> {
|
|
try {
|
|
if (process.env.PASEO_DESKTOP_CLI === "1") {
|
|
throw new Error(
|
|
"Cannot open a desktop project while running in desktop CLI passthrough mode.",
|
|
);
|
|
}
|
|
|
|
const desktopApp = findDesktopApp();
|
|
if (!desktopApp) {
|
|
throw new Error(
|
|
"Paseo desktop app not found. Install it from https://github.com/getpaseo/paseo/releases",
|
|
);
|
|
}
|
|
|
|
if (process.platform === "darwin") {
|
|
// -n forces a new instance even if the app is already running.
|
|
// The new instance hits requestSingleInstanceLock(), fails, and relays
|
|
// the argv to the first instance via the second-instance event.
|
|
// -g keeps the terminal in the foreground (better CLI UX).
|
|
// Without -n, macOS just activates the existing window and drops --args.
|
|
spawnDetached("open", ["-n", "-g", "-a", desktopApp, "--args", projectPath]);
|
|
return;
|
|
}
|
|
|
|
spawnDetached(desktopApp, [projectPath]);
|
|
} catch (error) {
|
|
const message = error instanceof Error ? error.message : String(error);
|
|
process.stderr.write(`${message}\n`);
|
|
process.exitCode = 1;
|
|
}
|
|
}
|