fix: interrupt scaffold drain and desktop CLI bin layout

This commit is contained in:
Mohamed Boudra
2026-03-21 18:37:51 +07:00
parent c35d649f6d
commit 7395fc6fd1
10 changed files with 509 additions and 90 deletions
@@ -158,6 +158,8 @@ function buildDesktopDaemonCorsOriginsEnv(): string | undefined {
.filter((value) => value.length > 0)
);
origins.add("paseo://app");
const devServerUrl = process.env.EXPO_DEV_URL ?? DEFAULT_ELECTRON_DEV_SERVER_URL;
try {
const parsed = new URL(devServerUrl);
@@ -479,7 +481,7 @@ function getCliSymlinkInstructions(): CliSymlinkInstructions {
if (process.platform === "darwin") {
const appBundle = electronExePath.replace(/\/Contents\/MacOS\/.+$/, "");
const cliPath = path.join(appBundle, "Contents", "MacOS", cliShimFilename);
const cliPath = path.join(appBundle, "Contents", "Resources", "bin", cliShimFilename);
return {
title: "Add paseo to your shell",
detail: "Create a symlink to the bundled Paseo CLI shim.",
@@ -488,18 +490,20 @@ function getCliSymlinkInstructions(): CliSymlinkInstructions {
}
if (process.platform === "win32") {
const cliPath = path.join(path.dirname(electronExePath), "resources", "bin", cliShimFilename);
return {
title: "Add paseo to your PATH",
detail: "Add the Paseo installation directory to your system PATH so paseo.cmd is available.",
commands: `setx PATH "%PATH%;${path.dirname(electronExePath)}"`,
commands: `setx PATH "%PATH%;${path.dirname(cliPath)}"`,
};
}
// Linux
const cliPath = path.join(path.dirname(electronExePath), "resources", "bin", cliShimFilename);
return {
title: "Add paseo to your shell",
detail: "Create a symlink to the bundled Paseo CLI shim.",
commands: `sudo ln -sf "${path.join(path.dirname(electronExePath), cliShimFilename)}" /usr/local/bin/paseo`,
commands: `sudo ln -sf "${cliPath}" /usr/local/bin/paseo`,
};
}
+39 -5
View File
@@ -1,6 +1,7 @@
import path from "node:path";
import { pathToFileURL } from "node:url";
import { existsSync } from "node:fs";
import { app, BrowserWindow, nativeImage } from "electron";
import { app, BrowserWindow, nativeImage, net, protocol } from "electron";
import { registerDaemonManager } from "./daemon/daemon-manager.js";
import { parseCliPassthroughArgsFromArgv, runCliPassthroughCommand } from "./daemon/runtime-paths.js";
import { closeAllTransportSessions } from "./daemon/local-transport.js";
@@ -11,8 +12,13 @@ import { registerOpenerHandlers } from "./features/opener.js";
import { setupApplicationMenu } from "./features/menu.js";
const DEV_SERVER_URL = process.env.EXPO_DEV_URL ?? "http://localhost:8081";
const APP_SCHEME = "paseo";
app.setName("Paseo");
protocol.registerSchemesAsPrivileged([
{ scheme: APP_SCHEME, privileges: { standard: true, secure: true, supportFetchAPI: true } },
]);
// ---------------------------------------------------------------------------
// Window creation
// ---------------------------------------------------------------------------
@@ -21,12 +27,12 @@ function getPreloadPath(): string {
return path.join(__dirname, "preload.js");
}
function getProductionEntryPath(): string {
function getAppDistDir(): string {
if (app.isPackaged) {
return path.join(process.resourcesPath, "app-dist", "index.html");
return path.join(process.resourcesPath, "app-dist");
}
return path.resolve(__dirname, "../../app/dist/index.html");
return path.resolve(__dirname, "../../app/dist");
}
function getWindowIconPath(): string | null {
@@ -94,7 +100,7 @@ async function createMainWindow(): Promise<void> {
return;
}
await mainWindow.loadFile(getProductionEntryPath());
await mainWindow.loadURL(`${APP_SCHEME}://app/`);
}
// ---------------------------------------------------------------------------
@@ -148,6 +154,34 @@ async function bootstrap(): Promise<void> {
}
await app.whenReady();
const appDistDir = getAppDistDir();
protocol.handle(APP_SCHEME, (request) => {
const { pathname, search, hash } = new URL(request.url);
const decodedPath = decodeURIComponent(pathname);
// Chromium can occasionally request the exported entrypoint directly.
// Canonicalize it back to the route URL so Expo Router sees `/`, not `/index.html`.
if (decodedPath.endsWith("/index.html")) {
const normalizedPath = decodedPath.slice(0, -"/index.html".length) || "/";
return Response.redirect(`${APP_SCHEME}://app${normalizedPath}${search}${hash}`, 307);
}
const filePath = path.join(appDistDir, decodedPath);
const relativePath = path.relative(appDistDir, filePath);
if (relativePath.startsWith("..") || path.isAbsolute(relativePath)) {
return new Response("Not found", { status: 404 });
}
// SPA fallback: serve index.html for routes without a file extension
if (!relativePath || !path.extname(relativePath)) {
return net.fetch(pathToFileURL(path.join(appDistDir, "index.html")).toString());
}
return net.fetch(pathToFileURL(filePath).toString());
});
applyAppIcon();
setupApplicationMenu();
ensureNotificationCenterRegistration();