Files
paseo/packages/desktop/src/daemon/desktop-packaging.test.ts
T
Mohamed BoudraandGitHub 76a5edb020 Open existing agents from links and the CLI (#2324)
* feat(desktop): open existing agents from links

Register a stable agent deep link and route it through the existing Desktop window. Add a matching CLI command that resolves the local server and activates the requested agent without creating or messaging it.

* fix(desktop): recover agent link delivery
2026-07-22 18:31:12 +02:00

150 lines
5.3 KiB
TypeScript

import { spawnSync } from "node:child_process";
import {
chmodSync,
copyFileSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
function writeExecutable(filePath: string, contents: string): void {
writeFileSync(filePath, contents, "utf8");
chmodSync(filePath, 0o755);
}
function createFakeMacBundle(options: { includeHelper: boolean }): {
root: string;
shimPath: string;
} {
const root = mkdtempSync(join(tmpdir(), "paseo-cli-shim-test-"));
const appPath = join(root, "Paseo.app");
const contentsPath = join(appPath, "Contents");
const resourcesPath = join(contentsPath, "Resources");
const shimPath = join(resourcesPath, "bin", "paseo");
const mainPath = join(contentsPath, "MacOS", "Paseo");
const helperPath = join(
contentsPath,
"Frameworks",
"Paseo Helper.app",
"Contents",
"MacOS",
"Paseo Helper",
);
mkdirSync(dirname(shimPath), { recursive: true });
mkdirSync(dirname(mainPath), { recursive: true });
copyFileSync(join(packageRoot, "bin", "paseo"), shimPath);
chmodSync(shimPath, 0o755);
writeExecutable(mainPath, "#!/bin/sh\necho main-executable\n");
if (options.includeHelper) {
mkdirSync(dirname(helperPath), { recursive: true });
writeExecutable(
helperPath,
[
"#!/bin/sh",
'printf "helper env=%s/%s cli=%s\\n" "$ELECTRON_RUN_AS_NODE" "$PASEO_NODE_ENV" "$PASEO_CLI"',
'printf "args=%s\\n" "$*"',
"",
].join("\n"),
);
}
return { root, shimPath };
}
describe("desktop packaging", () => {
it("unpacks server zsh shell integration files for external shells", () => {
const config = readFileSync(join(packageRoot, "electron-builder.yml"), "utf8");
expect(config).toContain(
"node_modules/@getpaseo/server/dist/server/terminal/shell-integration/**/*",
);
expect(config).not.toContain(
"node_modules/@getpaseo/server/dist/src/terminal/shell-integration/**/*",
);
});
it("excludes package debug/source files from the packaged app", () => {
const config = readFileSync(join(packageRoot, "electron-builder.yml"), "utf8");
expect(config).toContain("!**/*.map");
expect(config).toContain("!node_modules/@getpaseo/*/src/**");
expect(config).toContain("!node_modules/@getpaseo/**/*.test.*");
expect(config).toContain("!node_modules/@getpaseo/**/*.spec.*");
});
it("excludes the bundled daemon web UI from the packaged app", () => {
const config = readFileSync(join(packageRoot, "electron-builder.yml"), "utf8");
expect(config).toContain("!node_modules/@getpaseo/server/dist/server/web-ui/**");
});
it("registers Paseo agent links with the operating system", () => {
const config = readFileSync(join(packageRoot, "electron-builder.yml"), "utf8");
expect(config).toContain("name: Paseo agent link");
expect(config).toContain("- paseo");
});
// 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("*");
}
});
it("launches the packaged macOS CLI through Helper instead of the main app executable", () => {
if (process.platform === "win32") return;
const bundle = createFakeMacBundle({ includeHelper: true });
try {
const result = spawnSync(bundle.shimPath, ["--version"], { encoding: "utf8" });
expect(result.status).toBe(0);
expect(result.stdout).toContain(`helper env=1/production cli=${bundle.shimPath}`);
expect(result.stdout).toContain("node-entrypoint-runner.js");
expect(result.stdout).toContain("node-script");
expect(result.stdout).toContain("@getpaseo/cli/dist/index.js");
expect(result.stdout).toContain("--version");
expect(result.stdout).not.toContain("main-executable");
} finally {
rmSync(bundle.root, { recursive: true, force: true });
}
});
it("fails packaged macOS CLI startup when Helper is missing", () => {
if (process.platform === "win32") return;
const bundle = createFakeMacBundle({ includeHelper: false });
try {
const result = spawnSync(bundle.shimPath, ["--version"], { encoding: "utf8" });
expect(result.status).toBe(1);
expect(result.stderr).toContain("Bundled Paseo Helper executable not found");
expect(result.stdout).not.toContain("main-executable");
} finally {
rmSync(bundle.root, { recursive: true, force: true });
}
});
});