From f4d9357a1281057952023f04f8118adddb792391 Mon Sep 17 00:00:00 2001 From: Devin Date: Wed, 14 Oct 2026 09:24:37 +0900 Subject: [PATCH 1/3] fix(startup): launch the consented Desktop path, never a bundle-id lookup The ensure cold start launched macOS Desktop with `open -b com.opencodex.desktop`, letting LaunchServices pick any registered bundle claiming the identifier while the durable ownership record's install id was discarded at the wiring edge. Launch only the path the consented installation itself registered as its Start-at-Login program, and only while the app-local install id still matches the recorded claim: resolve the OpenCodex LaunchAgent's ProgramArguments to an executable inside a real app bundle, verify the bundle's own CFBundleIdentifier, and `open` that path. A claim whose registration, install-id, or executable evidence is absent, malformed or stale refuses the launch instead of guessing. Desktop-claimed installs without a login registration lose the automatic cold start and get the existing open-manually guidance instead of an unverifiable launch. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- src/cli/ensure-desktop-cold-start.ts | 60 +++++++++++-- structure/gui-and-management-api.md | 2 +- .../cli/cli-ensure-desktop-cold-start.test.ts | 90 +++++++++++++++++-- 3 files changed, 140 insertions(+), 12 deletions(-) diff --git a/src/cli/ensure-desktop-cold-start.ts b/src/cli/ensure-desktop-cold-start.ts index dd7a94de2c4..15236ce8097 100644 --- a/src/cli/ensure-desktop-cold-start.ts +++ b/src/cli/ensure-desktop-cold-start.ts @@ -10,8 +10,9 @@ * * Rules, in order: * - A running Desktop app is the supervisor. Wait for its proxy; never start a competing one. - * - A durable Desktop claim with no running app: start Desktop (hidden, as a login launch would) - * where that is possible, then wait. Elsewhere, fail with guidance. A missing Desktop process + * - A durable Desktop claim with no running app: start Desktop (hidden, by the path its own + * login registration recorded — LaunchServices never picks the bundle) where that is + * verifiable, then wait. Elsewhere, fail with guidance. A missing Desktop process * never authorizes a CLI proxy by itself; handing ownership to the CLI stays an explicit * `ocx service install` consent step. * - Ownership or the process list that cannot be read is not evidence of absence: refuse. @@ -22,7 +23,7 @@ */ import { execFileSync } from "node:child_process"; import { randomUUID } from "node:crypto"; -import { existsSync, linkSync, readdirSync, readFileSync, readlinkSync, statSync, unlinkSync, writeFileSync } from "node:fs"; +import { accessSync, constants, existsSync, linkSync, readdirSync, readFileSync, readlinkSync, realpathSync, statSync, unlinkSync, writeFileSync } from "node:fs"; import { homedir } from "node:os"; import { basename, dirname, join, win32 } from "node:path"; import { getConfigDir } from "../config/paths"; @@ -220,11 +221,58 @@ export function inspectDesktopProcess(options: DesktopProcessOptions = {}): Desk } } -/** Start the macOS app hidden, the same way its login item does. */ -export function launchDesktopApp(options: { platform?: NodeJS.Platform; run?: Execute } = {}): boolean { +const DESKTOP_BUNDLE_ID = "com.opencodex.desktop"; +const DESKTOP_PLIST_LABEL = "OpenCodex"; + +/** + * The app bundle the consented installation itself registered as its Start-at-Login program — + * not whichever bundle LaunchServices would resolve for our identifier. `open -b` answers a + * mutable per-user registry: any planted bundle claiming {@link DESKTOP_BUNDLE_ID} could be + * selected and executed in the real app's place. The recorded claim, this app's own install-id + * store, and the login registration it wrote are the durable evidence of which installation + * that is; a claim whose registration is absent, malformed, stale or points off a Desktop + * executable refuses the launch instead of guessing. + */ +function consentedDesktopBundle(home: string, installId: string, run: Execute): string | null { + const localId = readFileSync( + join(home, "Library", "Application Support", DESKTOP_BUNDLE_ID, "install-id"), "utf8", + ).trim(); + if (!localId || localId !== installId) return null; + const plistPath = realpathSync(join(home, "Library", "LaunchAgents", `${DESKTOP_PLIST_LABEL}.plist`)); + const registration = JSON.parse( + run("/usr/bin/plutil", ["-convert", "json", "-o", "-", plistPath], 3_000), + ) as { Label?: unknown; ProgramArguments?: unknown; Program?: unknown }; + const args = registration.ProgramArguments; + // The shapes auto-launch writes: [program] or [program, "--autostart"]; see the migration + // rules in desktop/src-tauri/src/login_registration.rs, which classify anything else Leave. + if (registration.Label !== DESKTOP_PLIST_LABEL || !Array.isArray(args) + || (args.length !== 1 && args.length !== 2) || typeof args[0] !== "string" + || (args.length === 2 && args[1] !== "--autostart") + || (registration.Program !== undefined && registration.Program !== args[0])) return null; + const executable = realpathSync(args[0]); + if (!isDesktopExecutable(executable, "darwin")) return null; + accessSync(executable, constants.X_OK); + const bundle = dirname(dirname(dirname(executable))); + const info = JSON.parse( + run("/usr/bin/plutil", ["-convert", "json", "-o", "-", join(bundle, "Contents", "Info.plist")], 3_000), + ) as { CFBundleIdentifier?: unknown }; + return info.CFBundleIdentifier === DESKTOP_BUNDLE_ID ? bundle : null; +} + +/** Start the consented macOS app hidden, by the path its own login registration recorded. */ +export function launchDesktopApp(options: { + platform?: NodeJS.Platform; + run?: Execute; + home?: string; + ownership?: () => ReturnType; +} = {}): boolean { if ((options.platform ?? process.platform) !== "darwin") return false; try { - (options.run ?? execute)("/usr/bin/open", ["-g", "-b", "com.opencodex.desktop", "--args", "--autostart"], 10_000); + const owner = (options.ownership ?? resolveServiceOwnership)(); + if (owner.kind !== "owned" || owner.ownership.owner !== "desktop") return false; + const bundle = consentedDesktopBundle(options.home ?? homedir(), owner.ownership.installId, options.run ?? execute); + if (bundle === null) return false; + (options.run ?? execute)("/usr/bin/open", ["-g", bundle, "--args", "--autostart"], 10_000); return true; } catch { return false; } } diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index 944c840582e..467e5573e6a 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -499,7 +499,7 @@ unvalidated Bun builds is unchanged (`src/lib/bun-stream-caps.ts`). ## Startup safety -Startup safety credits macOS and Linux `desktop` protection only after matching the durable app ownership or live supervision, an enabled login registration (macOS: loaded in this session or registered for the next login; Linux: enabled XDG autostart entry), and live app-to-bundled-proxy process relationship. This does not claim an independently installed CLI service. Missing or stale evidence remains at risk. The durable desktop claim remains visible when identity, login registration, or supervision fails; service install and repair controls and their copyable command stay disabled while the app owns or supervises the runtime. Shim controls stay enabled because `ocx ensure` (`src/cli/ensure-desktop-cold-start.ts`) waits for a running Desktop (matched by executable identity), relaunches a claimed macOS Desktop under a pid-held launch lease, and otherwise fails rather than starting a CLI proxy, including when ownership or the process list cannot be read. Under Desktop the shim installer child gets the login-shell PATH (`src/server/login-shell-path.ts`); an intact overlay not active on the dashboard's PATH reports `shimActivationPending` and shows the activation step instead of Repair. The Desktop row says "Running · Start at Login not verified" (`desktopBadgeKey` in `gui/src/startup-health-ui.ts`) for a fresh reading of a running owned or supervised app without verified login registration, and "Installed but unhealthy" otherwise; failed or stale readings never get the narrower label. Recovery guidance asks the user to check Start at Login and, when it is already checked, the system's login-item allowance. Compiled startup probes and the dashboard installer child (`runCliInstall` in `src/server/startup-action-control.ts`) use the standalone-aware self-launch argument builder. +Startup safety credits macOS and Linux `desktop` protection only after matching the durable app ownership or live supervision, an enabled login registration (macOS: loaded in this session or registered for the next login; Linux: enabled XDG autostart entry), and live app-to-bundled-proxy process relationship. This does not claim an independently installed CLI service. Missing or stale evidence remains at risk. The durable desktop claim remains visible when identity, login registration, or supervision fails; service install and repair controls and their copyable command stay disabled while the app owns or supervises the runtime. Shim controls stay enabled because `ocx ensure` (`src/cli/ensure-desktop-cold-start.ts`) waits for a running Desktop (matched by executable identity), relaunches a claimed macOS Desktop under a pid-held launch lease, and otherwise fails rather than starting a CLI proxy, including when ownership or the process list cannot be read. The relaunch runs the path the consented installation's own login registration recorded, after the app-local install id is checked against the recorded claim; a bundle-identifier lookup never chooses the launch target, so a claim whose registration or id evidence cannot be verified is refused rather than guessed. Under Desktop the shim installer child gets the login-shell PATH (`src/server/login-shell-path.ts`); an intact overlay not active on the dashboard's PATH reports `shimActivationPending` and shows the activation step instead of Repair. The Desktop row says "Running · Start at Login not verified" (`desktopBadgeKey` in `gui/src/startup-health-ui.ts`) for a fresh reading of a running owned or supervised app without verified login registration, and "Installed but unhealthy" otherwise; failed or stale readings never get the narrower label. Recovery guidance asks the user to check Start at Login and, when it is already checked, the system's login-item allowance. Compiled startup probes and the dashboard installer child (`runCliInstall` in `src/server/startup-action-control.ts`) use the standalone-aware self-launch argument builder. **Startup safety** is reachable by route (`/#startup`) and rendered by the app, but it is not a sidebar entry: it is entered from the dashboard's startup-state row, which links there whether the diff --git a/tests/cli/cli-ensure-desktop-cold-start.test.ts b/tests/cli/cli-ensure-desktop-cold-start.test.ts index 0e0289ce2e1..2e8ef78de49 100644 --- a/tests/cli/cli-ensure-desktop-cold-start.test.ts +++ b/tests/cli/cli-ensure-desktop-cold-start.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { mkdirSync, mkdtempSync, readdirSync, readFileSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"; +import { mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { @@ -158,11 +158,91 @@ describe("Desktop process detection by executable identity", () => { expect(inspectDesktopProcess({ platform: "win32", env, run: () => { throw new Error("denied"); } })).toBe("unknown"); }); - test("only macOS launches Desktop, hidden, through its bundle identifier", () => { + const owned = { + kind: "owned" as const, + ownership: { owner: "desktop" as const, installId: "install-a", consentGeneration: 1 }, + revision: 3, + }; + /** A fake consented install: app-local install-id, a LaunchAgent plist, and a bundle on disk. */ + function fakeDesktopHome(options: { + installId?: string; args?: string[]; bareArgs?: boolean; label?: string; identifier?: string; + plist?: "present" | "absent"; executable?: "present" | "absent"; + } = {}) { + const home = mkdtempSync(join(tmpdir(), "ocx-desktop-home-")); + const bundle = join(home, "Applications", "OpenCodex.app"); + const executable = join(bundle, "Contents", "MacOS", "opencodex-desktop"); + mkdirSync(join(bundle, "Contents", "MacOS"), { recursive: true }); + if (options.executable !== "absent") writeFileSync(executable, ""); + writeFileSync(join(bundle, "Contents", "Info.plist"), ""); + const store = join(home, "Library", "Application Support", "com.opencodex.desktop"); + mkdirSync(store, { recursive: true }); + if (options.installId !== undefined) writeFileSync(join(store, "install-id"), `${options.installId}\n`); + mkdirSync(join(home, "Library", "LaunchAgents"), { recursive: true }); + if (options.plist !== "absent") writeFileSync(join(home, "Library", "LaunchAgents", "OpenCodex.plist"), ""); const seen: string[][] = []; - expect(launchDesktopApp({ platform: "darwin", run: (command, args) => { seen.push([command, ...args]); return ""; } })).toBe(true); - expect(seen).toEqual([["/usr/bin/open", "-g", "-b", "com.opencodex.desktop", "--args", "--autostart"]]); - expect(launchDesktopApp({ platform: "darwin", run: () => { throw new Error("no app"); } })).toBe(false); + const run = (command: string, args: string[]) => { + seen.push([command, ...args]); + const target = args[args.length - 1] ?? ""; + if (command !== "/usr/bin/plutil") return ""; + if (target.endsWith(`${"OpenCodex"}.plist`)) { + return JSON.stringify({ + Label: options.label ?? "OpenCodex", + ProgramArguments: options.bareArgs ? [executable] : (options.args ?? [executable, "--autostart"]), + }); + } + if (target.endsWith("Info.plist")) return JSON.stringify({ CFBundleIdentifier: options.identifier ?? "com.opencodex.desktop" }); + return ""; + }; + return { home, bundle, seen, run }; + } + const launch = (fixture: ReturnType, ownership = () => owned) => + launchDesktopApp({ platform: "darwin", home: fixture.home, ownership, run: fixture.run }); + + test("a Desktop claim launches the app its own login registration recorded, never a bundle-id lookup", () => { + const fixture = fakeDesktopHome({ installId: "install-a" }); + try { + expect(launch(fixture)).toBe(true); + expect(fixture.seen.at(-1)).toEqual(["/usr/bin/open", "-g", realpathSync(fixture.bundle), "--args", "--autostart"]); + expect(fixture.seen.flat().includes("-b")).toBe(false); + } finally { removeTreeWithRetry(fixture.home); } + }); + + test("the registration shape auto-launch writes without --autostart is still the consented path", () => { + const fixture = fakeDesktopHome({ installId: "install-a", bareArgs: true }); + try { + expect(launch(fixture)).toBe(true); + expect(fixture.seen.at(-1)).toEqual(["/usr/bin/open", "-g", realpathSync(fixture.bundle), "--args", "--autostart"]); + } finally { removeTreeWithRetry(fixture.home); } + }); + + test("a claim without matching install-id, registration or Desktop executable refuses the launch", () => { + for (const fixture of [ + fakeDesktopHome({ installId: "other-install" }), + fakeDesktopHome({ installId: "install-a", plist: "absent" }), + fakeDesktopHome({ installId: "install-a", label: "OtherApp" }), + fakeDesktopHome({ installId: "install-a", args: [join(tmpdir(), "elsewhere", "opencodex-desktop")] }), + fakeDesktopHome({ installId: "install-a", args: ["executable-only-name"] }), + fakeDesktopHome({ installId: "install-a", identifier: "com.example.fake" }), + ]) { + try { + expect(launch(fixture)).toBe(false); + expect(fixture.seen.flat().includes("com.opencodex.desktop")).toBe(false); + expect(fixture.seen[0]?.[0]).not.toBe("/usr/bin/open"); + } finally { removeTreeWithRetry(fixture.home); } + } + // install-id store absent entirely. + const noId = fakeDesktopHome(); + try { expect(launch(noId)).toBe(false); } finally { removeTreeWithRetry(noId.home); } + }); + + test("a claim that is not a recorded Desktop owner, or a failing lookup, never launches", () => { + const fixture = fakeDesktopHome({ installId: "install-a" }); + try { + expect(launch(fixture, () => ({ kind: "none", revision: 2 }))).toBe(false); + expect(launch(fixture, () => ({ kind: "owned", ownership: { owner: "cli", installId: "install-a", consentGeneration: 1 }, revision: 2 }))).toBe(false); + expect(fixture.seen).toEqual([]); + } finally { removeTreeWithRetry(fixture.home); } + expect(launchDesktopApp({ platform: "darwin", home: mkdtempSync(join(tmpdir(), "ocx-desktop-empty-")), ownership: () => owned, run: () => { throw new Error("no app"); } })).toBe(false); expect(launchDesktopApp({ platform: "linux", run: () => { throw new Error("must not run"); } })).toBe(false); }); }); From db904a2fa98d7d94b4bb79969970da62f7ea6465 Mon Sep 17 00:00:00 2001 From: Devin Date: Wed, 14 Oct 2026 09:45:03 +0900 Subject: [PATCH 2/3] test(startup): mark the fake Desktop executable executable on POSIX CI's Linux shard enforces X_OK where Windows ignores it; the fixture now chmod 0755s the stub binary so the launch-binding cases exercise the same accepted path on both platforms. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- tests/cli/cli-ensure-desktop-cold-start.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/cli/cli-ensure-desktop-cold-start.test.ts b/tests/cli/cli-ensure-desktop-cold-start.test.ts index 2e8ef78de49..edb1814fd65 100644 --- a/tests/cli/cli-ensure-desktop-cold-start.test.ts +++ b/tests/cli/cli-ensure-desktop-cold-start.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"; +import { chmodSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { @@ -172,7 +172,7 @@ describe("Desktop process detection by executable identity", () => { const bundle = join(home, "Applications", "OpenCodex.app"); const executable = join(bundle, "Contents", "MacOS", "opencodex-desktop"); mkdirSync(join(bundle, "Contents", "MacOS"), { recursive: true }); - if (options.executable !== "absent") writeFileSync(executable, ""); + if (options.executable !== "absent") { writeFileSync(executable, ""); chmodSync(executable, 0o755); } writeFileSync(join(bundle, "Contents", "Info.plist"), ""); const store = join(home, "Library", "Application Support", "com.opencodex.desktop"); mkdirSync(store, { recursive: true }); From 8281e0fed4bfa67454017ad06084599db43453af Mon Sep 17 00:00:00 2001 From: Epinephrine Date: Tue, 13 Oct 2026 18:19:35 -0700 Subject: [PATCH 3/3] fix(startup): reject registered paths without an app bundle suffix --- src/cli/ensure-desktop-cold-start.ts | 1 + structure/gui-and-management-api.md | 2 +- .../cli/cli-ensure-desktop-cold-start.test.ts | 22 +++++++++++++++++-- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/cli/ensure-desktop-cold-start.ts b/src/cli/ensure-desktop-cold-start.ts index 15236ce8097..43ceeffab2d 100644 --- a/src/cli/ensure-desktop-cold-start.ts +++ b/src/cli/ensure-desktop-cold-start.ts @@ -253,6 +253,7 @@ function consentedDesktopBundle(home: string, installId: string, run: Execute): if (!isDesktopExecutable(executable, "darwin")) return null; accessSync(executable, constants.X_OK); const bundle = dirname(dirname(dirname(executable))); + if (!bundle.endsWith(".app")) return null; const info = JSON.parse( run("/usr/bin/plutil", ["-convert", "json", "-o", "-", join(bundle, "Contents", "Info.plist")], 3_000), ) as { CFBundleIdentifier?: unknown }; diff --git a/structure/gui-and-management-api.md b/structure/gui-and-management-api.md index 467e5573e6a..7064505a941 100644 --- a/structure/gui-and-management-api.md +++ b/structure/gui-and-management-api.md @@ -499,7 +499,7 @@ unvalidated Bun builds is unchanged (`src/lib/bun-stream-caps.ts`). ## Startup safety -Startup safety credits macOS and Linux `desktop` protection only after matching the durable app ownership or live supervision, an enabled login registration (macOS: loaded in this session or registered for the next login; Linux: enabled XDG autostart entry), and live app-to-bundled-proxy process relationship. This does not claim an independently installed CLI service. Missing or stale evidence remains at risk. The durable desktop claim remains visible when identity, login registration, or supervision fails; service install and repair controls and their copyable command stay disabled while the app owns or supervises the runtime. Shim controls stay enabled because `ocx ensure` (`src/cli/ensure-desktop-cold-start.ts`) waits for a running Desktop (matched by executable identity), relaunches a claimed macOS Desktop under a pid-held launch lease, and otherwise fails rather than starting a CLI proxy, including when ownership or the process list cannot be read. The relaunch runs the path the consented installation's own login registration recorded, after the app-local install id is checked against the recorded claim; a bundle-identifier lookup never chooses the launch target, so a claim whose registration or id evidence cannot be verified is refused rather than guessed. Under Desktop the shim installer child gets the login-shell PATH (`src/server/login-shell-path.ts`); an intact overlay not active on the dashboard's PATH reports `shimActivationPending` and shows the activation step instead of Repair. The Desktop row says "Running · Start at Login not verified" (`desktopBadgeKey` in `gui/src/startup-health-ui.ts`) for a fresh reading of a running owned or supervised app without verified login registration, and "Installed but unhealthy" otherwise; failed or stale readings never get the narrower label. Recovery guidance asks the user to check Start at Login and, when it is already checked, the system's login-item allowance. Compiled startup probes and the dashboard installer child (`runCliInstall` in `src/server/startup-action-control.ts`) use the standalone-aware self-launch argument builder. +Startup safety credits macOS and Linux `desktop` protection only after matching the durable app ownership or live supervision, an enabled login registration (macOS: loaded in this session or registered for the next login; Linux: enabled XDG autostart entry), and live app-to-bundled-proxy process relationship. This does not claim an independently installed CLI service. Missing or stale evidence remains at risk. The durable desktop claim remains visible when identity, login registration, or supervision fails; service install and repair controls and their copyable command stay disabled while the app owns or supervises the runtime. Shim controls stay enabled because `ocx ensure` (`src/cli/ensure-desktop-cold-start.ts`) waits for a running Desktop (matched by executable identity), relaunches a claimed macOS Desktop under a pid-held launch lease, and otherwise fails rather than starting a CLI proxy, including when ownership or the process list cannot be read. The relaunch runs the path the consented installation's own login registration recorded, after the app-local install id is checked against the recorded claim and the resolved bundle path ends in `.app`; a bundle-identifier lookup never chooses the launch target, so a claim whose registration or id evidence cannot be verified is refused rather than guessed. Under Desktop the shim installer child gets the login-shell PATH (`src/server/login-shell-path.ts`); an intact overlay not active on the dashboard's PATH reports `shimActivationPending` and shows the activation step instead of Repair. The Desktop row says "Running · Start at Login not verified" (`desktopBadgeKey` in `gui/src/startup-health-ui.ts`) for a fresh reading of a running owned or supervised app without verified login registration, and "Installed but unhealthy" otherwise; failed or stale readings never get the narrower label. Recovery guidance asks the user to check Start at Login and, when it is already checked, the system's login-item allowance. Compiled startup probes and the dashboard installer child (`runCliInstall` in `src/server/startup-action-control.ts`) use the standalone-aware self-launch argument builder. **Startup safety** is reachable by route (`/#startup`) and rendered by the app, but it is not a sidebar entry: it is entered from the dashboard's startup-state row, which links there whether the diff --git a/tests/cli/cli-ensure-desktop-cold-start.test.ts b/tests/cli/cli-ensure-desktop-cold-start.test.ts index edb1814fd65..9ea14e22242 100644 --- a/tests/cli/cli-ensure-desktop-cold-start.test.ts +++ b/tests/cli/cli-ensure-desktop-cold-start.test.ts @@ -166,10 +166,10 @@ describe("Desktop process detection by executable identity", () => { /** A fake consented install: app-local install-id, a LaunchAgent plist, and a bundle on disk. */ function fakeDesktopHome(options: { installId?: string; args?: string[]; bareArgs?: boolean; label?: string; identifier?: string; - plist?: "present" | "absent"; executable?: "present" | "absent"; + plist?: "present" | "absent"; executable?: "present" | "absent"; bundleName?: string; } = {}) { const home = mkdtempSync(join(tmpdir(), "ocx-desktop-home-")); - const bundle = join(home, "Applications", "OpenCodex.app"); + const bundle = join(home, "Applications", options.bundleName ?? "OpenCodex.app"); const executable = join(bundle, "Contents", "MacOS", "opencodex-desktop"); mkdirSync(join(bundle, "Contents", "MacOS"), { recursive: true }); if (options.executable !== "absent") { writeFileSync(executable, ""); chmodSync(executable, 0o755); } @@ -215,6 +215,24 @@ describe("Desktop process detection by executable identity", () => { } finally { removeTreeWithRetry(fixture.home); } }); + test("a non-app directory refuses the launch without reading its Info.plist or waiting for a proxy", async () => { + for (const bundleName of ["OpenCodex", "OpenCodex.app.backup"]) { + const fixture = fakeDesktopHome({ installId: "install-a", bundleName }); + try { + const calls = fresh(); + const dependencies = deps({ ownership: "desktop", process: "absent" }, calls); + dependencies.launchDesktop = () => launch(fixture); + const result = await runDesktopColdStart(dependencies); + expect(result.kind === "failed" && result.message).toContain("Open OpenCodex manually"); + expect(calls.waits).toEqual([]); + expect(calls.released).toBe(1); + expect(fixture.seen).toHaveLength(1); + expect(fixture.seen[0]?.[0]).toBe("/usr/bin/plutil"); + expect(fixture.seen[0]?.at(-1)).toEndWith("OpenCodex.plist"); + } finally { removeTreeWithRetry(fixture.home); } + } + }); + test("a claim without matching install-id, registration or Desktop executable refuses the launch", () => { for (const fixture of [ fakeDesktopHome({ installId: "other-install" }),