diff --git a/CHANGELOG.md b/CHANGELOG.md index 241dee20380..71871bddd7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ Docs: https://docs.openclaw.ai ### Fixes -- Codex/app-server: resolve the managed `@openai/codex` package bin when package installs do not provide a nearby `.bin/codex` shim, avoiding false missing-binary startup failures. +- Codex/app-server: resolve managed binaries from bundled `dist` chunks and from the `@openai/codex` package bin when installs do not provide a nearby `.bin/codex` shim, avoiding false missing-binary startup failures. - Plugins/source checkout: discover source-only plugins such as Codex from the `extensions/*` workspace while using npm package excludes as the packaged-core boundary, removing the stale core-bundle metadata path. - Plugins/ClawHub: install ClawPack artifacts from the explicit npm-pack `.tgz` resolver path instead of the legacy ZIP-shaped placeholder route. Thanks @vincentkoc. - Control UI: allow deployments to configure grouped chat message max-width with a validated `gateway.controlUi.chatMessageMaxWidth` setting instead of patching bundled CSS after upgrades. Fixes #67935. Thanks @xiew4589-lang. diff --git a/extensions/codex/src/app-server/managed-binary.test.ts b/extensions/codex/src/app-server/managed-binary.test.ts index 78bcd5ec1ec..83aba0c5c9c 100644 --- a/extensions/codex/src/app-server/managed-binary.test.ts +++ b/extensions/codex/src/app-server/managed-binary.test.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { describe, expect, it, vi } from "vitest"; import type { CodexAppServerStartOptions } from "./config.js"; import { + __testing, resolveManagedCodexAppServerPaths, resolveManagedCodexAppServerStartOptions, } from "./managed-binary.js"; @@ -66,6 +67,16 @@ describe("managed Codex app-server binary", () => { ); }); + it("uses the package root when the resolver is bundled into a dist chunk", () => { + expect(__testing.resolveDefaultCodexPluginRoot("/repo/openclaw/dist")).toBe("/repo/openclaw"); + expect(__testing.resolveDefaultCodexPluginRoot("/repo/openclaw/dist-runtime")).toBe( + "/repo/openclaw", + ); + expect( + __testing.resolveDefaultCodexPluginRoot("/repo/openclaw/extensions/codex/src/app-server"), + ).toBe("/repo/openclaw/extensions/codex"); + }); + it("finds Codex in the package install root used by packaged plugins", async () => { const installRoot = path.join("/tmp", "openclaw-plugin-package", "codex"); const pluginRoot = path.join(installRoot, "dist", "extensions", "codex"); diff --git a/extensions/codex/src/app-server/managed-binary.ts b/extensions/codex/src/app-server/managed-binary.ts index 0228baefa1f..bcdc1479693 100644 --- a/extensions/codex/src/app-server/managed-binary.ts +++ b/extensions/codex/src/app-server/managed-binary.ts @@ -6,7 +6,8 @@ import { fileURLToPath } from "node:url"; import type { CodexAppServerStartOptions } from "./config.js"; import { MANAGED_CODEX_APP_SERVER_PACKAGE } from "./version.js"; -const CODEX_PLUGIN_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); +const CODEX_APP_SERVER_MODULE_DIR = path.dirname(fileURLToPath(import.meta.url)); +const CODEX_PLUGIN_ROOT = resolveDefaultCodexPluginRoot(CODEX_APP_SERVER_MODULE_DIR); type ManagedCodexAppServerPaths = { commandPath: string; @@ -76,6 +77,14 @@ function resolveManagedCodexAppServerCommandCandidates( ]; } +function resolveDefaultCodexPluginRoot(moduleDir: string): string { + const moduleBaseName = path.basename(moduleDir); + if (moduleBaseName === "dist" || moduleBaseName === "dist-runtime") { + return path.dirname(moduleDir); + } + return path.resolve(moduleDir, "..", ".."); +} + function resolveManagedCodexAppServerCandidateRoots( pluginRoot: string, platform: NodeJS.Platform, @@ -135,6 +144,10 @@ function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null; } +export const __testing = { + resolveDefaultCodexPluginRoot, +}; + function isDistExtensionRoot(pluginRoot: string, platform: NodeJS.Platform): boolean { const pathApi = pathForPlatform(platform); const extensionsDir = pathApi.dirname(pluginRoot);