mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
* fix(packaged): let a macOS headless runtime become the desktop in place
When Codex starts Open Design headlessly through MCP, macOS treats that
windowless process as the running app. Opening the app from the Dock,
Finder, Launchpad or `open -a` then only reactivates it, so no launcher
runs and no window ever appears (OPEND-3426).
On macOS the headless runtime now runs the desktop path with its window
deferred, and restores the window in the same process when the user
opens the app: through `activate`/`open-url` paired with the app becoming
active, or through SHOW from a newer launcher that sees a `restorable`
owner. The daemon, its port and connected MCP clients stay. A new outer
hands headless launches to the active payload and marks the launch as
managed; payloads started by older outers keep today's behavior.
Managed MCP registrations carry absolute sidecar endpoints for every mode
of the namespace, so the MCP process finds the daemon whichever mode owns
it, waits for a running owner instead of reopening it, and cold-starts
only when nothing runs. The daemon refreshes an existing Codex
registration on start and never installs one. A write interrupted by a
service restart now says it may or may not have been applied.
* fix(daemon): scope Codex refresh to its managed install
Require the existing registration's managed discovery marker to match before refreshing it, so local, stable, and prerelease installs cannot take over the global name from one another. Cover the unrelated-owner path without invoking a config mutation.\n\nGenerated-By: looper 0.11.2 (runner=fixer, agent=codex)
* fix(daemon): prove Codex registration ownership by data root and app
Tighten the ownership check from d2d47a5f8e. Matching the discovery
endpoints alone still let a copy of the app that reuses the installed
namespace take over the registration (the endpoints depend only on
channel, namespace and source), and it could never refresh a
registration written before the managed contract, which has no
discovery value and still names a payload version that cleanup removes.
Read the existing registration and rewrite it only when it serves the
same daemon data root and bootstraps the same installed app; the managed
marker is ignored so a pre-managed registration of this install still
counts. Anything else, including a registration that cannot be read, is
left untouched.
---------
Co-authored-by: Looper <looper@noreply.github.com>
182 lines
6.0 KiB
TypeScript
182 lines
6.0 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
acquirePackagedHeadlessStartup,
|
|
parsePackagedHeadlessRequest,
|
|
resolvePackagedMcpBootstrapLaunch,
|
|
runPackagedMcpActionAgainstExistingDaemon,
|
|
} from "../src/headless-runtime.js";
|
|
import { APP_KEYS, MCP_BOOTSTRAP_CONTRACT, SIDECAR_SOURCES } from "@open-design/sidecar-proto";
|
|
|
|
describe("parsePackagedHeadlessRequest", () => {
|
|
it("accepts a headless Codex MCP install request", () => {
|
|
expect(parsePackagedHeadlessRequest([
|
|
"--headless",
|
|
"--mcp-install",
|
|
"codex",
|
|
])).toEqual({
|
|
headless: true,
|
|
mcpInstallAgent: "codex",
|
|
});
|
|
});
|
|
|
|
it("rejects unsupported MCP install targets", () => {
|
|
expect(() => parsePackagedHeadlessRequest([
|
|
"--headless",
|
|
"--mcp-install",
|
|
"claude",
|
|
])).toThrow(/only supports codex/i);
|
|
});
|
|
});
|
|
|
|
describe("resolvePackagedMcpBootstrapLaunch", () => {
|
|
it("uses macOS open against the stable signed app bundle", () => {
|
|
expect(resolvePackagedMcpBootstrapLaunch({
|
|
currentExecutablePath:
|
|
"/private/payload/Open Design.app/Contents/MacOS/Open Design",
|
|
installedLaunchPath: "/Applications/Open Design.app",
|
|
platform: "darwin",
|
|
})).toEqual({
|
|
command: "/usr/bin/open",
|
|
args: [
|
|
"-g",
|
|
"-j",
|
|
"/Applications/Open Design.app",
|
|
"--args",
|
|
"--headless",
|
|
],
|
|
});
|
|
});
|
|
|
|
it("marks a managed outer's bootstrap without changing how it launches", () => {
|
|
const launch = resolvePackagedMcpBootstrapLaunch({
|
|
installedLaunchPath: "/Applications/Open Design.app",
|
|
managed: true,
|
|
platform: "darwin",
|
|
});
|
|
expect(launch).toEqual({
|
|
command: "/usr/bin/open",
|
|
args: ["-g", "-j", "/Applications/Open Design.app", "--args", "--headless", MCP_BOOTSTRAP_CONTRACT.MANAGED_ARG],
|
|
});
|
|
// Unmanaged launches (an older outer) keep today's exact registration.
|
|
expect(resolvePackagedMcpBootstrapLaunch({
|
|
installedLaunchPath: "/Applications/Open Design.app",
|
|
platform: "darwin",
|
|
}).args).not.toContain(MCP_BOOTSTRAP_CONTRACT.MANAGED_ARG);
|
|
});
|
|
|
|
it("invokes a non-macOS installed launcher directly", () => {
|
|
expect(resolvePackagedMcpBootstrapLaunch({
|
|
currentExecutablePath: "/tmp/payload/open-design",
|
|
installedLaunchPath: "/opt/open-design/open-design",
|
|
platform: "linux",
|
|
})).toEqual({
|
|
command: "/opt/open-design/open-design",
|
|
args: ["--headless"],
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("runPackagedMcpActionAgainstExistingDaemon", () => {
|
|
const launchStamp = {
|
|
app: APP_KEYS.DESKTOP,
|
|
channel: "beta",
|
|
mode: "headless",
|
|
namespace: "release-beta",
|
|
source: "packaged",
|
|
} as const;
|
|
|
|
it("installs through a healthy existing headless daemon without bootstrapping", async () => {
|
|
const installMcp = vi.fn(async () => undefined);
|
|
const getStatus = vi.fn(async () => ({
|
|
state: "running",
|
|
url: "http://127.0.0.1:7457",
|
|
}));
|
|
|
|
await expect(runPackagedMcpActionAgainstExistingDaemon(
|
|
{ headless: true, mcpInstallAgent: "codex" },
|
|
launchStamp,
|
|
{ getStatus: getStatus as never, installMcp },
|
|
)).resolves.toBe(true);
|
|
expect(getStatus).toHaveBeenCalledWith(
|
|
{ ...launchStamp, app: APP_KEYS.DAEMON },
|
|
{ timeoutMs: 350 },
|
|
);
|
|
expect(installMcp).toHaveBeenCalledWith("http://127.0.0.1:7457");
|
|
});
|
|
|
|
it("leaves bootstrap to the caller when no healthy daemon exists", async () => {
|
|
const installMcp = vi.fn(async () => undefined);
|
|
await expect(runPackagedMcpActionAgainstExistingDaemon(
|
|
{ headless: true, mcpInstallAgent: "codex" },
|
|
launchStamp,
|
|
{
|
|
getStatus: vi.fn(async () => ({ state: "stopped", url: "http://127.0.0.1:7457" })) as never,
|
|
installMcp,
|
|
},
|
|
)).resolves.toBe(false);
|
|
expect(installMcp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("finds a packaged-source daemon for a tools-pack request", async () => {
|
|
const installMcp = vi.fn(async () => undefined);
|
|
const getStatus = vi.fn(async (candidate: { source: string }) => candidate.source === SIDECAR_SOURCES.PACKAGED
|
|
? { state: "running", url: "http://127.0.0.1:7457" }
|
|
: Promise.reject(new Error("missing endpoint")));
|
|
await expect(runPackagedMcpActionAgainstExistingDaemon(
|
|
{ headless: true, mcpInstallAgent: "codex" },
|
|
{ ...launchStamp, source: SIDECAR_SOURCES.TOOLS_PACK },
|
|
{ getStatus: getStatus as never, installMcp },
|
|
)).resolves.toBe(true);
|
|
expect(getStatus.mock.calls.map(([candidate]) => candidate.source)).toEqual([
|
|
SIDECAR_SOURCES.TOOLS_PACK,
|
|
SIDECAR_SOURCES.PACKAGED,
|
|
]);
|
|
expect(installMcp).toHaveBeenCalledWith("http://127.0.0.1:7457");
|
|
});
|
|
});
|
|
|
|
describe("acquirePackagedHeadlessStartup", () => {
|
|
function createDependencies(failAt: "mcp" | "none") {
|
|
const closed: string[] = [];
|
|
return {
|
|
closed,
|
|
dependencies: {
|
|
confirmRuntime: vi.fn(async () => undefined),
|
|
installMcp: vi.fn(async () => {
|
|
if (failAt === "mcp") throw new Error("MCP install failed");
|
|
}),
|
|
startSidecars: vi.fn(async () => ({
|
|
close: async () => {
|
|
closed.push("sidecars");
|
|
},
|
|
currentWebUrl: () => "http://127.0.0.1:7456",
|
|
daemon: {
|
|
desktopAuthGateActive: false,
|
|
state: "running" as const,
|
|
url: "http://127.0.0.1:7457",
|
|
},
|
|
web: { state: "running" as const, url: "http://127.0.0.1:7456" },
|
|
})),
|
|
},
|
|
};
|
|
}
|
|
|
|
it("closes sidecars when MCP installation fails", async () => {
|
|
const { closed, dependencies } = createDependencies("mcp");
|
|
|
|
await expect(acquirePackagedHeadlessStartup(dependencies)).rejects.toThrow(
|
|
"MCP install failed",
|
|
);
|
|
|
|
expect(closed).toEqual(["sidecars"]);
|
|
});
|
|
|
|
it("returns a shutdown handle that closes sidecars", async () => {
|
|
const { closed, dependencies } = createDependencies("none");
|
|
const handle = await acquirePackagedHeadlessStartup(dependencies);
|
|
await handle.shutdown();
|
|
expect(closed).toEqual(["sidecars"]);
|
|
});
|
|
});
|