mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 20:35:39 -06:00
fix(codex): prefer ChatGPT desktop resources (#103470)
Co-authored-by: Hiroshi Tanaka <solavrc@gmail.com>
This commit is contained in:
committed by
GitHub
parent
9e74dabad2
commit
0f3fb9ea58
@@ -107,17 +107,20 @@ With this config, OpenClaw checks Codex app-server before each Codex-mode
|
||||
turn. If Computer Use is missing but Codex app-server has already discovered
|
||||
an installable marketplace, OpenClaw asks Codex app-server to install or
|
||||
re-enable the plugin and reload MCP servers. On macOS, when no matching
|
||||
marketplace is registered and the standard Codex app bundle exists, OpenClaw
|
||||
marketplace is registered and a standard desktop app bundle exists, OpenClaw
|
||||
also tries to register the bundled Codex marketplace from
|
||||
`/Applications/Codex.app/Contents/Resources/plugins/openai-bundled` before it
|
||||
fails. If setup still cannot make the MCP server available, the turn fails
|
||||
before the thread starts.
|
||||
`/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled`, with
|
||||
`/Applications/Codex.app/Contents/Resources/plugins/openai-bundled` retained
|
||||
as a fallback for legacy standalone installs. If setup still cannot make the
|
||||
MCP server available, the turn fails before the thread starts.
|
||||
|
||||
After changing Computer Use config, use `/new` or `/reset` in the affected
|
||||
chat before testing if an existing Codex thread has already started.
|
||||
|
||||
On macOS managed stdio startup, OpenClaw prefers the signed desktop Codex app
|
||||
bundle at `/Applications/Codex.app/Contents/Resources/codex` when it exists.
|
||||
On macOS managed stdio startup, OpenClaw prefers the signed desktop app
|
||||
bundle at `/Applications/ChatGPT.app/Contents/Resources/codex`, then falls
|
||||
back to `/Applications/Codex.app/Contents/Resources/codex` for legacy
|
||||
standalone installs.
|
||||
That keeps Computer Use under the app bundle that owns the local
|
||||
desktop-control permissions. If the desktop app is not installed, OpenClaw
|
||||
falls back to the managed Codex binary installed beside the plugin. If an
|
||||
@@ -177,24 +180,27 @@ matches fail closed and ask you to set `marketplaceName` or
|
||||
|
||||
## Bundled macOS marketplace
|
||||
|
||||
Recent Codex desktop builds bundle Computer Use here:
|
||||
Current ChatGPT desktop builds bundle Computer Use here; legacy standalone
|
||||
Codex desktop builds use the same layout under `Codex.app`:
|
||||
|
||||
```text
|
||||
/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled/plugins/computer-use
|
||||
/Applications/Codex.app/Contents/Resources/plugins/openai-bundled/plugins/computer-use
|
||||
```
|
||||
|
||||
When `computerUse.autoInstall` is true and no marketplace containing
|
||||
`computer-use` is registered, OpenClaw tries to add the standard bundled
|
||||
marketplace root automatically:
|
||||
`computer-use` is registered, OpenClaw tries to add the first standard
|
||||
bundled marketplace root that exists:
|
||||
|
||||
```text
|
||||
/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled
|
||||
/Applications/Codex.app/Contents/Resources/plugins/openai-bundled
|
||||
```
|
||||
|
||||
You can also register it explicitly from a shell with Codex:
|
||||
|
||||
```bash
|
||||
codex plugin marketplace add /Applications/Codex.app/Contents/Resources/plugins/openai-bundled
|
||||
codex plugin marketplace add /Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled
|
||||
```
|
||||
|
||||
If you use a nonstandard Codex app path, run `/codex computer-use install
|
||||
@@ -311,10 +317,11 @@ MCP server are present, but the local Computer Use bridge did not answer.
|
||||
Quit or restart Codex Computer Use, relaunch Codex Desktop if needed, then
|
||||
retry in a fresh OpenClaw session. If the host previously ran Computer Use
|
||||
through an older managed Codex app-server, refresh the installed plugin from
|
||||
the desktop bundled marketplace:
|
||||
the desktop bundled marketplace (use the `Codex.app` path for standalone
|
||||
Codex desktop installs):
|
||||
|
||||
```text
|
||||
/codex computer-use install --source /Applications/Codex.app/Contents/Resources/plugins/openai-bundled
|
||||
/codex computer-use install --source /Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled
|
||||
```
|
||||
|
||||
**A Computer Use tool says `Native hook relay unavailable`.** The
|
||||
|
||||
@@ -247,6 +247,32 @@ describe("Codex Computer Use setup", () => {
|
||||
expectRequestMethodNotCalled(request, "plugin/install");
|
||||
});
|
||||
|
||||
it("does not inspect bundled app paths when a registered marketplace is ready", async () => {
|
||||
const request = createComputerUseRequest({ installed: true });
|
||||
const forbiddenCandidates = new Proxy(["/unused/bundled-marketplace"], {
|
||||
get() {
|
||||
throw new Error("bundled marketplace candidates must stay lazy");
|
||||
},
|
||||
});
|
||||
|
||||
const status = await ensureCodexComputerUse({
|
||||
pluginConfig: {
|
||||
computerUse: {
|
||||
enabled: true,
|
||||
autoInstall: true,
|
||||
},
|
||||
},
|
||||
request,
|
||||
defaultBundledMarketplacePaths: forbiddenCandidates,
|
||||
});
|
||||
|
||||
expectStatusFields(status, {
|
||||
ready: true,
|
||||
reason: "ready",
|
||||
});
|
||||
expectRequestMethodNotCalled(request, "marketplace/add");
|
||||
});
|
||||
|
||||
it("uses setup writes when auto-install needs to install", async () => {
|
||||
const request = createComputerUseRequest({ installed: false });
|
||||
|
||||
@@ -280,6 +306,9 @@ describe("Codex Computer Use setup", () => {
|
||||
path.join(os.tmpdir(), "openclaw-codex-bundled-marketplace-"),
|
||||
);
|
||||
cleanupPaths.push(bundledMarketplacePath);
|
||||
fs.mkdirSync(path.join(bundledMarketplacePath, "plugins", "computer-use"), {
|
||||
recursive: true,
|
||||
});
|
||||
const request = createBundledMarketplaceComputerUseRequest(bundledMarketplacePath);
|
||||
|
||||
const status = await ensureCodexComputerUse({
|
||||
@@ -290,7 +319,7 @@ describe("Codex Computer Use setup", () => {
|
||||
},
|
||||
},
|
||||
request,
|
||||
defaultBundledMarketplacePath: bundledMarketplacePath,
|
||||
defaultBundledMarketplacePaths: [bundledMarketplacePath],
|
||||
});
|
||||
|
||||
expectStatusFields(status, {
|
||||
@@ -308,6 +337,90 @@ describe("Codex Computer Use setup", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
label: "prefers ChatGPT.app when both desktop marketplaces exist",
|
||||
marketplaceIndexes: [0, 1],
|
||||
pluginIndexes: [0, 1],
|
||||
expectedIndex: 0,
|
||||
},
|
||||
{
|
||||
label: "uses ChatGPT.app when it is the only desktop marketplace",
|
||||
marketplaceIndexes: [0],
|
||||
pluginIndexes: [0],
|
||||
expectedIndex: 0,
|
||||
},
|
||||
{
|
||||
label: "falls back to legacy Codex.app when it is the only desktop marketplace",
|
||||
marketplaceIndexes: [1],
|
||||
pluginIndexes: [1],
|
||||
expectedIndex: 1,
|
||||
},
|
||||
{
|
||||
label: "skips a ChatGPT.app marketplace that does not contain Computer Use",
|
||||
marketplaceIndexes: [0, 1],
|
||||
pluginIndexes: [1],
|
||||
expectedIndex: 1,
|
||||
},
|
||||
{
|
||||
label: "does not add a marketplace when neither desktop bundle contains Computer Use",
|
||||
marketplaceIndexes: [0, 1],
|
||||
pluginIndexes: [],
|
||||
expectedIndex: undefined,
|
||||
},
|
||||
])("$label", async ({ marketplaceIndexes, pluginIndexes, expectedIndex }) => {
|
||||
const root = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-codex-desktop-bundles-"));
|
||||
cleanupPaths.push(root);
|
||||
const bundledMarketplacePaths = [
|
||||
path.join(root, "ChatGPT.app", "Contents", "Resources", "plugins", "openai-bundled"),
|
||||
path.join(root, "Codex.app", "Contents", "Resources", "plugins", "openai-bundled"),
|
||||
];
|
||||
for (const index of marketplaceIndexes) {
|
||||
fs.mkdirSync(bundledMarketplacePaths[index], { recursive: true });
|
||||
}
|
||||
for (const index of pluginIndexes) {
|
||||
fs.mkdirSync(path.join(bundledMarketplacePaths[index], "plugins", "computer-use"), {
|
||||
recursive: true,
|
||||
});
|
||||
}
|
||||
|
||||
const expectedPath =
|
||||
expectedIndex === undefined ? undefined : bundledMarketplacePaths[expectedIndex];
|
||||
const request = expectedPath
|
||||
? createBundledMarketplaceComputerUseRequest(expectedPath)
|
||||
: createEmptyMarketplaceComputerUseRequest();
|
||||
const setup = ensureCodexComputerUse({
|
||||
pluginConfig: {
|
||||
computerUse: {
|
||||
enabled: true,
|
||||
autoInstall: true,
|
||||
marketplaceDiscoveryTimeoutMs: 1,
|
||||
},
|
||||
},
|
||||
request,
|
||||
defaultBundledMarketplacePaths: bundledMarketplacePaths,
|
||||
});
|
||||
|
||||
if (!expectedPath) {
|
||||
await expectSetupErrorStatus(setup, {
|
||||
ready: false,
|
||||
reason: "marketplace_missing",
|
||||
});
|
||||
expectRequestMethodNotCalled(request, "marketplace/add");
|
||||
return;
|
||||
}
|
||||
|
||||
const status = await setup;
|
||||
expectStatusFields(status, {
|
||||
ready: true,
|
||||
reason: "ready",
|
||||
marketplaceName: "openai-bundled",
|
||||
});
|
||||
expect(request).toHaveBeenCalledWith("marketplace/add", {
|
||||
source: expectedPath,
|
||||
});
|
||||
});
|
||||
|
||||
it("allows auto-install from a configured local marketplace path", async () => {
|
||||
const request = createComputerUseRequest({ installed: false });
|
||||
|
||||
@@ -601,6 +714,9 @@ function createAmbiguousComputerUseRequest(): CodexComputerUseRequest {
|
||||
|
||||
function createEmptyMarketplaceComputerUseRequest(): CodexComputerUseRequest {
|
||||
return vi.fn(async (method: string) => {
|
||||
if (method === "experimentalFeature/enablement/set") {
|
||||
return { enablement: { plugins: true } };
|
||||
}
|
||||
if (method === "plugin/list") {
|
||||
return {
|
||||
marketplaces: [],
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* app-server sessions.
|
||||
*/
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { describeControlFailure } from "./capabilities.js";
|
||||
import type { CodexAppServerClient } from "./client.js";
|
||||
import {
|
||||
@@ -74,7 +75,7 @@ export type CodexComputerUseSetupParams = {
|
||||
timeoutMs?: number;
|
||||
signal?: AbortSignal;
|
||||
forceEnable?: boolean;
|
||||
defaultBundledMarketplacePath?: string;
|
||||
defaultBundledMarketplacePaths?: readonly string[];
|
||||
};
|
||||
|
||||
type MarketplaceRef =
|
||||
@@ -106,8 +107,11 @@ type PluginInspection =
|
||||
|
||||
const CURATED_MARKETPLACE_POLL_INTERVAL_MS = 2_000;
|
||||
const COMPUTER_USE_MARKETPLACE_NAME_PRIORITY = ["openai-bundled", "openai-curated", "local"];
|
||||
const DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH =
|
||||
"/Applications/Codex.app/Contents/Resources/plugins/openai-bundled";
|
||||
// ChatGPT.app is the current desktop owner; keep Codex.app as the legacy fallback.
|
||||
const DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATHS = [
|
||||
"/Applications/ChatGPT.app/Contents/Resources/plugins/openai-bundled",
|
||||
"/Applications/Codex.app/Contents/Resources/plugins/openai-bundled",
|
||||
] as const;
|
||||
|
||||
/** Reads Computer Use readiness without installing or mutating app-server state. */
|
||||
export async function readCodexComputerUseStatus(
|
||||
@@ -200,7 +204,7 @@ async function inspectCodexComputerUse(params: {
|
||||
signal?: AbortSignal;
|
||||
config: ResolvedCodexComputerUseConfig;
|
||||
installPlugin: boolean;
|
||||
defaultBundledMarketplacePath?: string;
|
||||
defaultBundledMarketplacePaths?: readonly string[];
|
||||
}): Promise<CodexComputerUseStatus> {
|
||||
const request = createComputerUseRequest(params);
|
||||
if (params.installPlugin) {
|
||||
@@ -214,7 +218,7 @@ async function inspectCodexComputerUse(params: {
|
||||
config: params.config,
|
||||
allowAdd: params.installPlugin,
|
||||
signal: params.signal,
|
||||
defaultBundledMarketplacePath: params.defaultBundledMarketplacePath,
|
||||
defaultBundledMarketplacePaths: params.defaultBundledMarketplacePaths,
|
||||
});
|
||||
if (!marketplace.marketplace) {
|
||||
return unavailableStatus(
|
||||
@@ -340,7 +344,7 @@ async function resolveMarketplaceRef(params: {
|
||||
config: ResolvedCodexComputerUseConfig;
|
||||
allowAdd: boolean;
|
||||
signal?: AbortSignal;
|
||||
defaultBundledMarketplacePath?: string;
|
||||
defaultBundledMarketplacePaths?: readonly string[];
|
||||
}): Promise<MarketplaceResolution> {
|
||||
let preferredMarketplaceName = params.config.marketplaceName;
|
||||
if (params.config.marketplaceSource && params.allowAdd) {
|
||||
@@ -358,14 +362,27 @@ async function resolveMarketplaceRef(params: {
|
||||
}
|
||||
|
||||
let candidates = await listComputerUseMarketplaceCandidates(params.request, params.config);
|
||||
if (candidates.length === 0 && shouldAddBundledComputerUseMarketplace(params)) {
|
||||
const bundledMarketplacePath =
|
||||
params.defaultBundledMarketplacePath ?? DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH;
|
||||
const added = await params.request<{ marketplaceName?: string }>("marketplace/add", {
|
||||
source: bundledMarketplacePath,
|
||||
} satisfies CodexRequestObject);
|
||||
preferredMarketplaceName ??= added.marketplaceName;
|
||||
candidates = await listComputerUseMarketplaceCandidates(params.request, params.config);
|
||||
if (
|
||||
candidates.length === 0 &&
|
||||
params.allowAdd &&
|
||||
usesDefaultMarketplaceDiscovery(params.config)
|
||||
) {
|
||||
// Most turns already have a registered marketplace. Probe app bundles only
|
||||
// on the empty auto-install path to keep ordinary startup free of filesystem I/O.
|
||||
const bundledMarketplacePath = (
|
||||
params.defaultBundledMarketplacePaths ?? DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATHS
|
||||
).find((candidatePath) =>
|
||||
// The signed desktop bundles publish plugins under this fixed marketplace layout.
|
||||
// Check the requested plugin before registering a source that would shadow the fallback.
|
||||
existsSync(path.join(candidatePath, "plugins", params.config.pluginName)),
|
||||
);
|
||||
if (bundledMarketplacePath) {
|
||||
const added = await params.request<{ marketplaceName?: string }>("marketplace/add", {
|
||||
source: bundledMarketplacePath,
|
||||
} satisfies CodexRequestObject);
|
||||
preferredMarketplaceName ??= added.marketplaceName;
|
||||
candidates = await listComputerUseMarketplaceCandidates(params.request, params.config);
|
||||
}
|
||||
}
|
||||
|
||||
const waitUntil = marketplaceDiscoveryWaitUntil(params);
|
||||
@@ -405,7 +422,10 @@ async function resolveMarketplaceRef(params: {
|
||||
};
|
||||
}
|
||||
const marketplace = candidates[0];
|
||||
return marketplace ? { marketplace } : {};
|
||||
if (marketplace) {
|
||||
return { marketplace };
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
async function listComputerUseMarketplaceCandidates(
|
||||
@@ -431,20 +451,8 @@ function blockUnsafeAutoInstallStatus(
|
||||
);
|
||||
}
|
||||
|
||||
function shouldAddBundledComputerUseMarketplace(params: {
|
||||
config: ResolvedCodexComputerUseConfig;
|
||||
allowAdd: boolean;
|
||||
defaultBundledMarketplacePath?: string;
|
||||
}): boolean {
|
||||
const bundledMarketplacePath =
|
||||
params.defaultBundledMarketplacePath ?? DEFAULT_CODEX_BUNDLED_MARKETPLACE_PATH;
|
||||
return (
|
||||
params.allowAdd &&
|
||||
!params.config.marketplaceSource &&
|
||||
!params.config.marketplacePath &&
|
||||
!params.config.marketplaceName &&
|
||||
existsSync(bundledMarketplacePath)
|
||||
);
|
||||
function usesDefaultMarketplaceDiscovery(config: ResolvedCodexComputerUseConfig): boolean {
|
||||
return !config.marketplaceSource && !config.marketplacePath && !config.marketplaceName;
|
||||
}
|
||||
|
||||
function findComputerUseMarketplaces(
|
||||
@@ -484,12 +492,7 @@ function marketplaceDiscoveryWaitUntil(params: {
|
||||
config: ResolvedCodexComputerUseConfig;
|
||||
allowAdd: boolean;
|
||||
}): number {
|
||||
if (
|
||||
params.allowAdd &&
|
||||
!params.config.marketplaceSource &&
|
||||
!params.config.marketplacePath &&
|
||||
!params.config.marketplaceName
|
||||
) {
|
||||
if (params.allowAdd && usesDefaultMarketplaceDiscovery(params.config)) {
|
||||
return Date.now() + params.config.marketplaceDiscoveryTimeoutMs;
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -28,6 +28,8 @@ function managedCommandPath(root: string, platform: NodeJS.Platform): string {
|
||||
}
|
||||
|
||||
const MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND = "/Applications/Codex.app/Contents/Resources/codex";
|
||||
const MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND =
|
||||
"/Applications/ChatGPT.app/Contents/Resources/codex";
|
||||
|
||||
describe("managed Codex app-server binary", () => {
|
||||
it("leaves explicit command overrides unchanged", async () => {
|
||||
@@ -43,10 +45,58 @@ describe("managed Codex app-server binary", () => {
|
||||
expect(pathExists).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("prefers the macOS desktop app bundle when it exists", async () => {
|
||||
it("prefers ChatGPT.app when both macOS desktop bundles exist", async () => {
|
||||
const pluginRoot = path.join("/tmp", "openclaw", "extensions", "codex");
|
||||
const paths = resolveManagedCodexAppServerPaths({ platform: "darwin", pluginRoot });
|
||||
const pluginLocalCommand = managedCommandPath(pluginRoot, "darwin");
|
||||
const pathExists = vi.fn(
|
||||
async (filePath: string) =>
|
||||
filePath === MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND ||
|
||||
filePath === MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND ||
|
||||
filePath === pluginLocalCommand,
|
||||
);
|
||||
|
||||
await expect(
|
||||
resolveManagedCodexAppServerStartOptions(startOptions("managed"), {
|
||||
platform: "darwin",
|
||||
pluginRoot,
|
||||
pathExists,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
...startOptions("managed"),
|
||||
command: MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND,
|
||||
commandSource: "resolved-managed",
|
||||
managedFallbackCommandPaths: [MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND, pluginLocalCommand],
|
||||
});
|
||||
expect(paths.commandPath).toBe(MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND);
|
||||
expect(paths.candidateCommandPaths).toContain(pluginLocalCommand);
|
||||
});
|
||||
|
||||
it("prefers the ChatGPT.app desktop bundle when Codex.app is absent", async () => {
|
||||
const pluginRoot = path.join("/tmp", "openclaw", "extensions", "codex");
|
||||
const pluginLocalCommand = managedCommandPath(pluginRoot, "darwin");
|
||||
const pathExists = vi.fn(
|
||||
async (filePath: string) =>
|
||||
filePath === MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND || filePath === pluginLocalCommand,
|
||||
);
|
||||
|
||||
await expect(
|
||||
resolveManagedCodexAppServerStartOptions(startOptions("managed"), {
|
||||
platform: "darwin",
|
||||
pluginRoot,
|
||||
pathExists,
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
...startOptions("managed"),
|
||||
command: MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND,
|
||||
commandSource: "resolved-managed",
|
||||
managedFallbackCommandPaths: [pluginLocalCommand],
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the legacy Codex.app desktop bundle when ChatGPT.app is absent", async () => {
|
||||
const pluginRoot = path.join("/tmp", "openclaw", "extensions", "codex");
|
||||
const pluginLocalCommand = managedCommandPath(pluginRoot, "darwin");
|
||||
const pathExists = vi.fn(
|
||||
async (filePath: string) =>
|
||||
filePath === MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND || filePath === pluginLocalCommand,
|
||||
@@ -64,11 +114,9 @@ describe("managed Codex app-server binary", () => {
|
||||
commandSource: "resolved-managed",
|
||||
managedFallbackCommandPaths: [pluginLocalCommand],
|
||||
});
|
||||
expect(paths.commandPath).toBe(MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND);
|
||||
expect(paths.candidateCommandPaths).toContain(pluginLocalCommand);
|
||||
});
|
||||
|
||||
it("falls back to the plugin-local bundled Codex binary on macOS", async () => {
|
||||
it("falls back to the plugin-local binary when neither desktop bundle exists", async () => {
|
||||
const pluginRoot = path.join("/tmp", "openclaw", "extensions", "codex");
|
||||
const pluginLocalCommand = managedCommandPath(pluginRoot, "darwin");
|
||||
const pathExists = vi.fn(async (filePath: string) => filePath === pluginLocalCommand);
|
||||
@@ -84,6 +132,7 @@ describe("managed Codex app-server binary", () => {
|
||||
command: pluginLocalCommand,
|
||||
commandSource: "resolved-managed",
|
||||
});
|
||||
expect(pathExists).toHaveBeenCalledWith(MACOS_DESKTOP_CHATGPT_APP_SERVER_COMMAND, "darwin");
|
||||
expect(pathExists).toHaveBeenCalledWith(MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND, "darwin");
|
||||
});
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@ import { MANAGED_CODEX_APP_SERVER_PACKAGE } from "./version.js";
|
||||
|
||||
const CODEX_APP_SERVER_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
|
||||
const CODEX_PLUGIN_ROOT = resolveDefaultCodexPluginRoot(CODEX_APP_SERVER_MODULE_DIR);
|
||||
const MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND = "/Applications/Codex.app/Contents/Resources/codex";
|
||||
// ChatGPT.app is the current desktop owner; keep Codex.app as the legacy fallback.
|
||||
const MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS = [
|
||||
"/Applications/ChatGPT.app/Contents/Resources/codex",
|
||||
"/Applications/Codex.app/Contents/Resources/codex",
|
||||
] as const;
|
||||
|
||||
type ManagedCodexAppServerPaths = {
|
||||
commandPath: string;
|
||||
@@ -89,7 +93,7 @@ function resolveManagedCodexAppServerCommandCandidates(
|
||||
}
|
||||
|
||||
function resolveDesktopCodexAppServerCommandCandidates(platform: NodeJS.Platform): string[] {
|
||||
return platform === "darwin" ? [MACOS_DESKTOP_CODEX_APP_SERVER_COMMAND] : [];
|
||||
return platform === "darwin" ? [...MACOS_DESKTOP_CODEX_APP_SERVER_COMMANDS] : [];
|
||||
}
|
||||
|
||||
function resolveDefaultCodexPluginRoot(moduleDir: string): string {
|
||||
|
||||
Reference in New Issue
Block a user