Files
paseo/packages/desktop/scripts/dev-runtime.mjs
T
Mohamed Boudra 6b13587943 fix(desktop): allow concurrent worktree dev instances
Desktop terminals can inherit another instance's profile path and debugging flags. Scope the profile to the current dev root and select an available debugging port so instances do not share global runtime state.
2026-07-29 16:40:50 +02:00

102 lines
3.0 KiB
JavaScript

#!/usr/bin/env node
import net from "node:net";
import path from "node:path";
import { fileURLToPath } from "node:url";
const DEFAULT_REMOTE_DEBUGGING_PORT = 9223;
export function resolveDevUserDataDir({ devRoot, inheritedUserDataDir, fallbackRoot }) {
if (devRoot) {
return path.join(devRoot, ".dev", "user-data");
}
if (inheritedUserDataDir) {
return inheritedUserDataDir;
}
if (!fallbackRoot) {
throw new Error("A dev root or fallback root is required");
}
return path.join(fallbackRoot, ".dev", "user-data");
}
export function buildElectronFlags(inheritedFlags, remoteDebuggingPort) {
const flags = (inheritedFlags ?? "")
.split(/\s+/)
.filter(Boolean)
.filter((flag) => !flag.startsWith("--remote-debugging-port="));
flags.push(`--remote-debugging-port=${remoteDebuggingPort}`);
return flags.join(" ");
}
function parsePort(value, name) {
const port = Number(value);
if (!Number.isInteger(port) || port < 1 || port > 65_535) {
throw new Error(`${name} must be an integer between 1 and 65535`);
}
return port;
}
export function findAvailablePort(preferredPort, host = "127.0.0.1") {
return new Promise((resolve, reject) => {
const server = net.createServer();
server.unref();
server.once("error", (error) => {
if (error.code === "EADDRINUSE" || error.code === "EACCES") {
resolve(findAvailablePort(0, host));
return;
}
reject(error);
});
server.listen(preferredPort, host, () => {
const address = server.address();
if (!address || typeof address === "string") {
server.close();
reject(new Error("Could not resolve an available CDP port"));
return;
}
server.close((error) => {
if (error) {
reject(error);
return;
}
resolve(address.port);
});
});
});
}
export async function selectRemoteDebuggingPort({
configuredPort,
preferredPort = DEFAULT_REMOTE_DEBUGGING_PORT,
findAvailablePort: findPort = findAvailablePort,
} = {}) {
if (configuredPort) {
return parsePort(configuredPort, "PASEO_ELECTRON_REMOTE_DEBUGGING_PORT");
}
return findPort(preferredPort);
}
export async function resolveDevRuntime(env = process.env) {
const remoteDebuggingPort = await selectRemoteDebuggingPort({
configuredPort: env.PASEO_ELECTRON_REMOTE_DEBUGGING_PORT,
});
return {
electronFlags: buildElectronFlags(env.PASEO_ELECTRON_FLAGS, remoteDebuggingPort),
remoteDebuggingPort,
userDataDir: resolveDevUserDataDir({
devRoot: env.PASEO_DEV_ROOT,
inheritedUserDataDir: env.PASEO_ELECTRON_USER_DATA_DIR,
fallbackRoot: env.PASEO_DEV_RUNTIME_FALLBACK_ROOT,
}),
};
}
const isMain = process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url);
if (isMain) {
try {
process.stdout.write(JSON.stringify(await resolveDevRuntime()));
} catch (error) {
console.error(`[dev-runtime] ${error instanceof Error ? error.message : String(error)}`);
process.exitCode = 1;
}
}