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
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
RESOURCES_DIR=$(CDPATH= cd -- "${SCRIPT_DIR}/.." && pwd)
# macOS: MacOS/Paseo is a sibling of Resources/
# Linux: the executable sits next to resources/
if [ -x "${RESOURCES_DIR}/../MacOS/Paseo" ]; then
APP_EXECUTABLE="${RESOURCES_DIR}/../MacOS/Paseo"
elif [ -x "${RESOURCES_DIR}/../Paseo" ]; then
APP_EXECUTABLE="${RESOURCES_DIR}/../Paseo"
else
echo "Bundled Paseo executable not found relative to ${RESOURCES_DIR}" >&2
exit 1
fi
CLI_ENTRY="${RESOURCES_DIR}/app.asar/node_modules/@getpaseo/cli/dist/index.js"
exec env ELECTRON_RUN_AS_NODE=1 "${APP_EXECUTABLE}" "${CLI_ENTRY}" "$@"
+15
View File
@@ -0,0 +1,15 @@
@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
set "RESOURCES_DIR=%SCRIPT_DIR%.."
set "APP_EXECUTABLE=%RESOURCES_DIR%\..\Paseo.exe"
set "CLI_ENTRY=%RESOURCES_DIR%\app.asar\node_modules\@getpaseo\cli\dist\index.js"
if not exist "%APP_EXECUTABLE%" (
echo Bundled Paseo executable not found at %APP_EXECUTABLE% 1>&2
exit /b 1
)
set "ELECTRON_RUN_AS_NODE=1"
"%APP_EXECUTABLE%" "%CLI_ENTRY%" %*
+9 -9
View File
@@ -19,26 +19,26 @@ mac:
notarize: true
entitlements: build/entitlements.mac.plist
entitlementsInherit: build/entitlements.mac.inherit.plist
extraFiles:
- from: scripts/paseo
to: MacOS/paseo
extraResources:
- from: bin/paseo
to: bin/paseo
target:
- dmg
- zip
linux:
category: Development
icon: assets
extraFiles:
- from: scripts/paseo
to: paseo
extraResources:
- from: bin/paseo
to: bin/paseo
target:
- AppImage
- tar.gz
win:
icon: assets/icon.ico
extraFiles:
- from: scripts/paseo.cmd
to: paseo.cmd
extraResources:
- from: bin/paseo.cmd
to: bin/paseo.cmd
target:
- nsis
- zip
-28
View File
@@ -1,28 +0,0 @@
#!/bin/sh
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
APP_EXECUTABLE="${SCRIPT_DIR}/Paseo"
if [ -d "${SCRIPT_DIR}/../Resources" ]; then
RESOURCES_DIR="${SCRIPT_DIR}/../Resources"
elif [ -d "${SCRIPT_DIR}/resources" ]; then
RESOURCES_DIR="${SCRIPT_DIR}/resources"
else
echo "Unable to locate Paseo resources directory next to ${SCRIPT_DIR}" >&2
exit 1
fi
CLI_ENTRY="${RESOURCES_DIR}/app.asar/node_modules/@getpaseo/cli/dist/index.js"
if [ ! -x "${APP_EXECUTABLE}" ]; then
echo "Bundled Paseo executable not found at ${APP_EXECUTABLE}" >&2
exit 1
fi
if [ ! -f "${CLI_ENTRY}" ]; then
echo "Bundled Paseo CLI entrypoint not found at ${CLI_ENTRY}" >&2
exit 1
fi
exec env ELECTRON_RUN_AS_NODE=1 "${APP_EXECUTABLE}" "${CLI_ENTRY}" "$@"
-19
View File
@@ -1,19 +0,0 @@
@echo off
setlocal
set "SCRIPT_DIR=%~dp0"
set "APP_EXECUTABLE=%SCRIPT_DIR%Paseo.exe"
set "CLI_ENTRY=%SCRIPT_DIR%resources\app.asar\node_modules\@getpaseo\cli\dist\index.js"
if not exist "%APP_EXECUTABLE%" (
echo Bundled Paseo executable not found at %APP_EXECUTABLE% 1>&2
exit /b 1
)
if not exist "%CLI_ENTRY%" (
echo Bundled Paseo CLI entrypoint not found at %CLI_ENTRY% 1>&2
exit /b 1
)
set "ELECTRON_RUN_AS_NODE=1"
"%APP_EXECUTABLE%" "%CLI_ENTRY%" %*
@@ -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();