fix(desktop): bundle @getpaseo/cli in packaged app

The CLI module was dropped from desktop deps in ba9ed8b9 (Knip
false-positive — runtime resolves it via createRequire, which static
analysis can't see). electron-builder then shipped 0.1.63-beta.{1,2}
without app.asar/node_modules/@getpaseo/cli, breaking every daemon
status check from the desktop wrapper. Splash startup never saw the
daemon as ready and retries collided with the live pid lock.

Re-declare the dep, and switch private-workspace internal @getpaseo/*
deps to "*" so npm always resolves the workspace sibling and never
falls back to the registry. Publishable workspaces (cli, server) keep
the root-version pin so their npm tarballs reference real ranges.

Update sync-workspace-versions.mjs to preserve that shape on release
bumps, and add a packaging assertion in desktop-packaging.test.ts that
fails fast if a runtime-required workspace dep is removed again.
This commit is contained in:
Mohamed Boudra
2026-04-26 17:10:21 +07:00
parent b7ee28659f
commit f27598af98
6 changed files with 33 additions and 9 deletions

View File

@@ -1,6 +1,6 @@
# Changelog
## 0.1.63-beta.2 - 2026-04-26
## 0.1.63-beta.3 - 2026-04-26
### Added

7
package-lock.json generated
View File

@@ -38420,8 +38420,8 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@floating-ui/react-native": "^0.10.7",
"@getpaseo/expo-two-way-audio": "0.1.63-beta.2",
"@getpaseo/highlight": "0.1.63-beta.2",
"@getpaseo/expo-two-way-audio": "*",
"@getpaseo/highlight": "*",
"@gorhom/bottom-sheet": "^5.2.6",
"@gorhom/portal": "^1.0.14",
"@react-native-async-storage/async-storage": "2.2.0",
@@ -38588,7 +38588,8 @@
"version": "0.1.63-beta.2",
"license": "AGPL-3.0-or-later",
"dependencies": {
"@getpaseo/server": "0.1.63-beta.2",
"@getpaseo/cli": "*",
"@getpaseo/server": "*",
"electron-log": "^5.4.3",
"electron-updater": "^6.6.2",
"ws": "^8.14.2"

View File

@@ -31,8 +31,8 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@floating-ui/react-native": "^0.10.7",
"@getpaseo/expo-two-way-audio": "0.1.63-beta.2",
"@getpaseo/highlight": "0.1.63-beta.2",
"@getpaseo/expo-two-way-audio": "*",
"@getpaseo/highlight": "*",
"@gorhom/bottom-sheet": "^5.2.6",
"@gorhom/portal": "^1.0.14",
"@react-native-async-storage/async-storage": "2.2.0",

View File

@@ -23,7 +23,8 @@
"typecheck": "tsgo --noEmit -p tsconfig.json"
},
"dependencies": {
"@getpaseo/server": "0.1.63-beta.2",
"@getpaseo/cli": "*",
"@getpaseo/server": "*",
"electron-log": "^5.4.3",
"electron-updater": "^6.6.2",
"ws": "^8.14.2"

View File

@@ -16,4 +16,21 @@ describe("desktop packaging", () => {
"node_modules/@getpaseo/server/dist/src/terminal/shell-integration/**/*",
);
});
// electron-builder packs production dependencies declared in package.json into
// app.asar. Runtime code in runtime-paths.ts and bin/paseo dynamically resolves
// these workspace packages by string, so static analysis (TypeScript, Knip) cannot
// see the link. If a runtime-required workspace dep is dropped from
// dependencies, the build still succeeds but ships a broken bundle. This
// assertion is the safety net.
it("declares all workspace packages required at runtime", () => {
const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")) as {
dependencies?: Record<string, string>;
};
const deps = pkg.dependencies ?? {};
for (const required of ["@getpaseo/cli", "@getpaseo/server"]) {
expect(deps[required], `${required} must be declared in dependencies`).toBe("*");
}
});
});

View File

@@ -54,6 +54,11 @@ for (const workspacePath of workspacePaths) {
}
}
// Private workspaces (app, desktop) keep "*" for internal deps so npm always
// resolves the local sibling, never a registry artifact. Publishable workspaces
// get the root version so their published tarballs reference real npm versions.
const internalDepRange = pkg.private === true ? "*" : rootVersion;
for (const section of dependencySections) {
const deps = pkg[section];
if (!deps || typeof deps !== "object") {
@@ -67,8 +72,8 @@ for (const workspacePath of workspacePaths) {
if (name === pkg.name) {
continue;
}
if (deps[name] !== rootVersion) {
deps[name] = rootVersion;
if (deps[name] !== internalDepRange) {
deps[name] = internalDepRange;
changed = true;
}
}